# Aave V4 Incentive Programs

Aave V4 reserves may offer additional incentives beyond base lending rates. These incentives are distributed through [Merkl](https://merkl.xyz/), a decentralized incentive distribution platform, or through _Points programs_ that reward users with points multipliers.

## Reward Types

Potential rewards are available in the `rewards` array of the [Reserve Summary](./reserves#reserve-data-structure).

<tabgroup>
<tab label="TypeScript">

```ts title="Reserve"
interface Reserve {
  // …
  summary: {
    rewards: Reward[];
    // …
  };
}

type Reward =
  | MerklSupplyReward
  | MerklBorrowReward
  | SupplyPointsReward
  | BorrowPointsReward;
```

</tab>
<tab label="GraphQL">

```graphql title="Reserve"
fragment Reserve on Reserve {
  # …
  summary {
    rewards {
      ... on MerklSupplyReward {
        ...MerklSupplyReward
      }

      ... on MerklBorrowReward {
        ...MerklBorrowReward
      }

      ... on SupplyPointsReward {
        ...SupplyPoints
      }

      ... on BorrowPointsReward {
        ...BorrowPoints
      }
    }
    # …
  }
}
```

</tab>
</tabgroup>
### Supply Rewards

Supply rewards can be present on suppliable reserves.

#### Merkl Supply Rewards

Merkl supply rewards represent an _extra APY_ on top of the base reserve supply APY. The accrued extra interest is paid in the specified payout token when the incentive campaign reaches its maturity.

<tabgroup>
<tab label="TypeScript">

```ts title="MerklSupplyReward"
interface MerklSupplyReward {
  __typename: "MerklSupplyReward";
  id: string;
  startDate: Date;
  endDate: Date;
  extraApy: PercentNumber;
  payoutToken: Erc20Token;
  criteria: MerklCriteria[];
  userEligible: boolean;
}
```

</tab>
<tab label="GraphQL">

```graphql title="MerklSupplyReward"
fragment MerklSupplyReward on MerklSupplyReward {
  __typename
  id
  startDate
  endDate
  extraApy {
    ...PercentNumber
  }
  payoutToken {
    ...Erc20Token
  }
  criteria {
    ...MerklCriteria
  }
  userEligible
}
```

</tab>
</tabgroup>
Where:

- `extraApy`: the additional APY earned on top of the base supply APY
- `payoutToken`: the token used to pay the extra interest accrued from the reward
- `criteria`: [eligibility requirements](#eligibility-criteria) for earning the reward

#### Points Supply Rewards

Some reserves participate in Points programs. Users who supply into these reserves accrue points over time proportional to their position size.

<tabgroup>
<tab label="TypeScript">

```ts title="SupplyPointsReward"
interface SupplyPointsReward {
  __typename: "SupplyPointsReward";
  id: string;
  program: PointsProgram;
  name: string;
  startDate: Date;
  endDate: Date | null;
  multiplier: number;
  criteria: PointsCriteria[];
  userEligible: boolean;
}
```

</tab>
<tab label="GraphQL">

```graphql title="SupplyPoints"
fragment SupplyPoints on SupplyPointsReward {
  __typename
  id
  program {
    ...PointsProgram
  }
  name
  startDate
  endDate
  multiplier
  criteria {
    ...PointsCriteria
  }
  userEligible
}
```

</tab>
</tabgroup>
Where:

- `program`: the [Points program](#points-program) issuing the reward
- `multiplier`: boost factor on the base points accrual rate
- `criteria`: [eligibility requirements](#eligibility-criteria) for earning the reward

### Borrow Rewards

Borrow rewards can be present on borrowable reserves.

#### Merkl Borrow Rewards

Merkl borrow rewards represent an _APY discount_ on the user’s borrow rate (which includes their [Risk Premium](../positions/conditions#user-risk-premium)). The accrued interest discount is paid in the specified payout token when the incentive campaign reaches its maturity.

<tabgroup>
<tab label="TypeScript">

```ts title="MerklBorrowReward"
interface MerklBorrowReward {
  __typename: "MerklBorrowReward";
  id: string;
  startDate: Date;
  endDate: Date;
  discountApy: PercentNumber;
  payoutToken: Erc20Token;
  criteria: MerklCriteria[];
  userEligible: boolean;
}
```

</tab>
<tab label="GraphQL">

```graphql title="MerklBorrowReward"
fragment MerklBorrowReward on MerklBorrowReward {
  __typename
  id
  startDate
  endDate
  discountApy {
    ...PercentNumber
  }
  payoutToken {
    ...Erc20Token
  }
  criteria {
    ...MerklCriteria
  }
  userEligible
}
```

</tab>
</tabgroup>
Where:

- `discountApy`: the APY discount applied to the user’s borrow rate
- `payoutToken`: the token used to pay the reward
- `criteria`: [eligibility requirements](#eligibility-criteria) for earning the reward

#### Points Borrow Rewards

Similarly, borrowing from certain reserves can accrue points in a Points program.

<tabgroup>
<tab label="TypeScript">

```ts title="BorrowPointsReward"
interface BorrowPointsReward {
  __typename: "BorrowPointsReward";
  id: string;
  program: PointsProgram;
  name: string;
  startDate: Date;
  endDate: Date | null;
  multiplier: number;
  criteria: PointsCriteria[];
  userEligible: boolean;
}
```

</tab>
<tab label="GraphQL">

```graphql title="BorrowPoints"
fragment BorrowPoints on BorrowPointsReward {
  __typename
  id
  program {
    ...PointsProgram
  }
  name
  startDate
  endDate
  multiplier
  criteria {
    ...PointsCriteria
  }
  userEligible
}
```

</tab>
</tabgroup>
Where:

- `program`: the [Points program](#points-program) issuing the reward
- `multiplier`: boost factor on the base points accrual rate
- `criteria`: [eligibility requirements](#eligibility-criteria) for earning the reward

### Points Program

A `PointsProgram` represents a loyalty or incentive system. Users accumulate points over time based on their supply or borrow position. The `externalUrl` links to the program’s website where users can view their accumulated points.

<tabgroup>
<tab label="TypeScript">

```ts title="PointsProgram"
interface PointsProgram {
  __typename: "PointsProgram";
  id: string;
  name: string;
  externalUrl: string | null;
  iconUrl: string | null;
}
```

</tab>
<tab label="GraphQL">

```graphql title="PointsProgram"
fragment PointsProgram on PointsProgram {
  __typename
  id
  name
  externalUrl
  iconUrl
}
```

</tab>
</tabgroup>
### Eligibility Criteria

Each reward may have eligibility criteria that users must meet. Both Merkl and Points criteria share the same shape (`id`, `text`, `userPassed`) but use distinct GraphQL types.

<tabgroup>
<tab label="TypeScript">

```ts title="MerklCriteria"
// Merkl rewards use MerklCriteria
interface MerklCriteria {
  __typename: "MerklGenericCriteria";
  id: string;
  text: string;
  userPassed: boolean;
}

// Points rewards use PointsCriteria
interface PointsCriteria {
  __typename: "PointsGenericCriteria";
  id: string;
  text: string;
  userPassed: boolean;
}
```

</tab>
<tab label="GraphQL">

```graphql title="MerklCriteria"
fragment MerklCriteria on MerklCriteria {
  ... on MerklGenericCriteria {
    __typename
    id
    text
    userPassed
  }
}

fragment PointsCriteria on PointsCriteria {
  ... on PointsGenericCriteria {
    __typename
    id
    text
    userPassed
  }
}
```

</tab>
</tabgroup>
## Matured Rewards

Rewards become claimable when their incentive campaign reaches maturity. Campaigns are often renewed upon reaching their end date, so users should check for new reward opportunities periodically.

### Claimable Rewards

Fetch the user’s matured rewards that are ready to claim.

Use the `useUserClaimableRewards` hook (or the imperative `useUserClaimableRewardsAction` variant) in React, the `userClaimableRewards` action in TypeScript, or the `userClaimableRewards` query in GraphQL to fetch all rewards the user can claim.

<tabgroup>
<tab label="React">

```tsx title="Loading State"
import {
  chainId,
  useUserClaimableRewards,
  type EvmAddress,
} from "@aave/react";

function ClaimableRewards({ user }: { user: EvmAddress }) {
  const { data, loading, error } = useUserClaimableRewards({
    user,
    chainIds: [chainId(1)],
  });

  if (loading) return <div>Loading…</div>;

  if (error) return <div>Error: {error.message}</div>;

  if (data.length === 0) return <div>No claimable rewards</div>;

  return (
    <div>
      {data.map((reward) => (
        <div key={reward.id}>
          Amount: {reward.claimable.amount.value.toFixed(2)}
          Claim until: {reward.claimUntil.toLocaleDateString()}
        </div>
      ))}
    </div>
  );
}
```

```tsx title="React Suspense"
import {
  chainId,
  useUserClaimableRewards,
  type EvmAddress,
} from "@aave/react";

function ClaimableRewards({ user }: { user: EvmAddress }) {
  const { data } = useUserClaimableRewards({
    user,
    chainIds: [chainId(1)],
    suspense: true,
  });

  if (data.length === 0) return <div>No claimable rewards</div>;

  return (
    <div>
      {data.map((reward) => (
        <div key={reward.id}>
          Amount: {reward.claimable.amount.value.toFixed(2)}
          Claim until: {reward.claimUntil.toLocaleDateString()}
        </div>
      ))}
    </div>
  );
}
```

```tsx title="Imperative Read"
import {
  chainId,
  useUserClaimableRewardsAction,
  type EvmAddress,
} from "@aave/react";

// …

const [fetch, { called, data, error, loading }] =
  useUserClaimableRewardsAction();

const handler = async (user: EvmAddress) => {
  const result = await fetch({
    user,
    chainIds: [chainId(1)],
  });

  if (result.isOk()) {
    console.log(result.value); // UserClaimableReward[]
  } else {
    console.error(result.error);
  }
};
```

</tab>
<tab label="TypeScript">

```ts title="Claimable Rewards"
import { userClaimableRewards } from "@aave/client/actions";
import { chainId, evmAddress } from "@aave/client";

import { client } from "./client";

const result = await userClaimableRewards(client, {
  user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
  chainIds: [chainId(1)],
});

if (result.isErr()) {
  console.error("claimable rewards error:", result.error);
} else {
  // result.value: UserClaimableReward[]
  console.log(result.value);
}
```

</tab>
<tab label="GraphQL">

```graphql title="Query"
query {
  userClaimableRewards(
    request: { 
      user: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
      chainIds: [1]
    }
  ) {
    __typename
    ... on UserMerklClaimableReward {
      id
      claimChainId
      claimable {
        token {
          address
          info {
            symbol
            decimals
          }
          chain {
            chainId
          }
        }
        amount {
          value
          decimals
          onChainValue
        }
        exchange(currency: USD) {
          value
          symbol
        }
      }
      startDate
      endDate
      claimUntil
    }
  }
}
```

</tab>
</tabgroup>
> [!NOTE]
> `useUserClaimableRewardsAction` doesn’t watch for updates. Use it for
> on-demand fresh data, such as in an event handler.

An empty array means the user has no claimable rewards.

The `UserMerklClaimableReward` type contains details about each claimable reward:

<tabgroup>
<tab label="TypeScript">

```ts title="UserMerklClaimableReward"
interface UserMerklClaimableReward {
  __typename: "UserMerklClaimableReward";
  id: RewardId;
  claimChainId: ChainId;
  claimable: Erc20Amount;
  startDate: Date;
  endDate: Date;
  claimUntil: Date;
}
```

</tab>
<tab label="GraphQL">

```graphql title="UserMerklClaimableReward"
fragment UserMerklClaimableReward on UserMerklClaimableReward {
  __typename
  id
  claimChainId
  claimable {
    ...Erc20Amount
  }
  startDate
  endDate
  claimUntil
}
```

</tab>
</tabgroup>
Where:

- `id`: unique identifier for the reward (used when claiming)
- `claimChainId`: the chain on which the reward is claimed
- `claimable`: the claimable token amount
- `startDate`: when the reward period started
- `endDate`: when the reward period ended
- `claimUntil`: deadline to claim the reward

### Claim Rewards

Once you have claimable rewards, collect them individually or all at once in a single transaction.

> [!NOTE]
> Claimed rewards can stay in the claimable list until Merkl reports them as
> claimed, which can take a while.

To claim rewards with AaveKit, follow these steps.

#### 1. Configure Wallet Integration

In React, first instantiate the `useSendTransaction` hook for the [wallet library of your choice](../getting-started/react#integrations).

<tabgroup>
<tab label="React">

```tsx title="Viem"
import { useWalletClient } from "wagmi";
import { useSendTransaction } from "@aave/react/viem";

// …

const { data: wallet } = useWalletClient();
const [sendTransaction] = useSendTransaction(wallet);
```

</tab>
</tabgroup>
#### 2. Define the Claim Flow

Then, in React, use the `useClaimRewards` hook to prepare the claim operation.

<tabgroup>
<tab label="React">

```tsx title="Basic Usage"
import { useClaimRewards } from "@aave/react";

const [claim, { loading, error }] = useClaimRewards((transaction) =>
  sendTransaction(transaction),
);
```

```tsx title="Update Component State"
import { useClaimRewards } from "@aave/react";
import { useState } from "react";

const [status, setStatus] = useState("");

const [claim, { loading, error }] = useClaimRewards((transaction) => {
  setStatus("Please sign the claim transaction in your wallet");
  return sendTransaction(transaction).andTee(() => setStatus("Claiming…"));
});
```

```tsx title="Cancel Flow"
import { useClaimRewards } from "@aave/react";

const [claim, { loading, error }] = useClaimRewards(
  (transaction, { cancel }) => {
    if (window.confirm("Are you sure you want to claim?") === false) {
      return cancel("User cancelled the operation");
    }
    return sendTransaction(transaction);
  },
);
```

</tab>
</tabgroup>
#### 3. Execute the Claim Operation

Then, execute the claim operation with the reward IDs from the claimable rewards.

- **TypeScript**: Use the `claimRewards` action.
- **GraphQL**: Use the `claimRewards` query.

<tabgroup>
<tab label="React">

```tsx title="Claim Rewards"
import {
  chainId,
  useUserClaimableRewards,
  evmAddress,
} from "@aave/react";

const { data: claimableRewards } = useUserClaimableRewards({
  user: evmAddress(wallet.account.address),
  chainIds: [chainId(1)],
  suspense: true,
});

const execute = async () => {
  if (claimableRewards.length > 0) {
    const result = await claim({
      ids: claimableRewards.map((reward) => reward.id),
      user: evmAddress(wallet.account.address),
      chainId: chainId(1),
    });

    // …
  }
};
```

</tab>
<tab label="TypeScript">

```ts title="Claim Rewards"
import { userClaimableRewards, claimRewards } from "@aave/client/actions";
import { chainId, evmAddress } from "@aave/client";

import { client } from "./client";

const result = await userClaimableRewards(client, {
  user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
  chainIds: [chainId(1)],
}).andThen((rewards) =>
  claimRewards(client, {
    ids: rewards.map((reward) => reward.id),
    user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
    chainId: chainId(1),
  }),
);

// …
```

```ts title="client.ts"
import { AaveClient } from "@aave/client";

export const client = AaveClient.create();
```

</tab>
<tab label="GraphQL">

```graphql title="Query"
query {
  claimRewards(
    request: {
      ids: ["reward-id-1", "reward-id-2"]
      user: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"
      chainId: 1
    }
  ) {
    to
    from
    data
    value
    chainId
  }
}
```

</tab>
</tabgroup>
#### 4. Process the Transaction Request

In TypeScript, use `sendWith` for the [wallet library of your choice](../getting-started/typescript#integrations) to send the transaction, and await indexing with `client.waitForTransaction`.

<tabgroup>
<tab label="TypeScript">

```ts title="Viem"
import { userClaimableRewards, claimRewards } from "@aave/client/actions";
import { sendWith } from "@aave/client/viem";
import { chainId, evmAddress } from "@aave/client";

import { client } from "./client";
import { wallet } from "./wallet";

const result = await userClaimableRewards(client, {
  user: evmAddress(wallet.account.address),
  chainIds: [chainId(1)],
})
  .andThen((rewards) =>
    claimRewards(client, {
      ids: rewards.map((reward) => reward.id),
      user: evmAddress(wallet.account.address),
      chainId: chainId(1),
    }),
  )
  .andThen(sendWith(wallet))
  .andThen(client.waitForTransaction);
```

```ts title="client.ts"
import { AaveClient } from "@aave/client";

export const client = AaveClient.create();
```

```ts title="wallet.ts"
import { createWalletClient, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

export const wallet = createWalletClient({
  account: privateKeyToAccount(process.env.PRIVATE_KEY as Hex),
  chain: mainnet,
  transport: http(),
});
```

</tab>
</tabgroup>
In GraphQL, send the returned transaction using the wallet associated with the `from` address.

<tabgroup>
<tab label="GraphQL">

```json title="Response"
{
  "data": {
    "claimRewards": {
      "to": "0x1234567890abcdef1234567890abcdef12345678",
      "from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
      "data": "0x…",
      "value": "0",
      "chainId": 1
    }
  }
}
```

</tab>
</tabgroup>
#### 5. Handle the Result

Finally, in React and TypeScript, handle the result.

<tabgroup>
<tab label="React">

```tsx title="Example"
const execute = async () => {
  const result = await claim(/* … */);

  if (result.isErr()) {
    switch (result.error.name) {
      case "CancelError":
        // The user cancelled the operation
        return;

      case "SigningError":
        console.error(
          `Failed to sign the transaction: ${result.error.message}`,
        );
        break;

      case "TimeoutError":
        console.error(`Transaction timed out: ${result.error.message}`);
        break;

      case "TransactionError":
        console.error(`Transaction failed: ${result.error.message}`);
        break;

      case "UnexpectedError":
        console.error(result.error.message);
        break;
    }
    return;
  }

  console.log("Rewards claimed successfully with hash:", result.value.txHash);
};
```

</tab>
<tab label="TypeScript">

```ts title="Example"
if (result.isErr()) {
  switch (result.error.name) {
    case "CancelError":
      // The user cancelled the operation
      return;

    case "SigningError":
      console.error(`Failed to sign the transaction: ${result.error.message}`);
      break;

    case "TimeoutError":
      console.error(`Transaction timed out: ${result.error.message}`);
      break;

    case "TransactionError":
      console.error(`Transaction failed: ${result.error.message}`);
      break;

    case "UnexpectedError":
      console.error(result.error.message);
      break;
  }
} else {
  console.log("Rewards claimed successfully with hash:", result.value.txHash);
}
```

</tab>
</tabgroup>
