# EVM

This guide shows how to claim onchain token rewards from the Fuul protocol on EVM networks. The flow is: [get claim checks](https://docs.fuul.xyz/developer-guide/claiming-onchain-rewards/get-claim-checks) from the API, read the fee from the contract, and submit a claim transaction.

{% hint style="info" %}
Before following this guide, make sure you've [fetched your claim checks](https://docs.fuul.xyz/developer-guide/claiming-onchain-rewards/get-claim-checks) using `@fuul/sdk`.
{% endhint %}

## 1. Understand the ClaimCheck struct

The `claim` function on the smart contract accepts an array of `ClaimCheck` structs:

```solidity
struct ClaimCheck {
    address projectAddress;
    address to;
    address currency;
    IFuulProject.TokenType currencyType;
    uint256 amount;
    ClaimReason reason;
    uint256 tokenId;
    uint256 deadline;
    bytes32 proof;
    bytes[] signatures;
}
```

| Enum          | Values                                                 |
| ------------- | ------------------------------------------------------ |
| `TokenType`   | `0` = NATIVE, `1` = ERC20, `2` = ERC721, `3` = ERC1155 |
| `ClaimReason` | `0` = AFFILIATE\_PAYOUT, `1` = END\_USER\_PAYOUT       |

The `claimChecks` array can contain multiple elements belonging to different projects and currencies.

## 2. Read the claim fee

The protocol charges a small native token fee per claim. Read it dynamically from the `FuulFactory` contract — **never hardcode it**:

```typescript
const feesInfo = await publicClient.readContract({
  address: '0xa0080A60EE9f1985151161Fa6b09652Dc46afdEF', // FuulFactory
  abi: fuulFactoryAbi,
  functionName: 'getFeesInformation',
  args: [projectAddress],
});

const totalFee = feesInfo.nativeUserClaimFee * BigInt(claimChecks.length);
```

## 3. Submit the claim transaction

All claims go through the `FuulManager` contract. Here's a complete example using viem/wagmi:

```typescript
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi';

const FUUL_MANAGER = '0x8a0836dA623ea1083c85acB958DeEa3716b39dc6';

// Transform API response to contract-ready format
const contractChecks = claimChecks.map((check) => ({
  projectAddress: check.project_address,
  to: check.to,
  currency: check.currency,
  currencyType: check.currency_type,
  amount: BigInt(check.amount),
  reason: check.reason,
  tokenId: BigInt(check.token_id),
  deadline: BigInt(check.deadline),
  proof: check.proof,
  signatures: check.signatures,
}));

// Submit with fee
writeContract({
  address: FUUL_MANAGER,
  abi: fuulManagerAbi,
  functionName: 'claim',
  args: [contractChecks],
  value: totalFee,
});
```

## Contract addresses

All supported networks use the same addresses:

| Contract        | Address                                      |
| --------------- | -------------------------------------------- |
| **FuulManager** | `0x8a0836dA623ea1083c85acB958DeEa3716b39dc6` |
| **FuulFactory** | `0xa0080A60EE9f1985151161Fa6b09652Dc46afdEF` |

| Network  | Chain ID |
| -------- | -------- |
| Ethereum | 1        |
| Arbitrum | 42161    |
| Base     | 8453     |
| HyperEVM | 999      |
| Optimism | 10       |

{% hint style="warning" %}
Always read the claim fee from the contract using `getFeesInformation()`. The fee is configurable per project and may change.
{% endhint %}
