AdvancedV3RewardDistributor

0xd4bd2b452fa9139e7989bc09aef69fd64a2f996f

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
14,594 bytes
ABI entries
72
31 read · 10 write
License
none

Contract information

Address
0xd4bd2b452fa9139e7989bc09aef69fd64a2f996f
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x6522a6f0cc…c1b7a4ec61

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (31)

CLAIM_AUTHORIZATION_TYPEHASH()bytes32
DISTRIBUTOR_KIND()bytes32
DISTRIBUTOR_VERSION()uint16
MAX_LEAF_COUNT()uint256
REWARD_SINK_KIND()bytes32
attester()address
checkpointedBlockHash(uint256)bytes32
correctionIdUsed(address, uint256, uint256)bool
destinationKind()bytes32
epochCorrection(address, uint256)bool, uint256, uint256
epochFunding(address, uint256)uint256, uint256, uint256, uint256
epochRange(address, uint256)uint256, uint256, bytes32
epochRoot(address, uint256)bytes32, uint256, uint256, bytes32, address, bytes32
epochRootCommitment(address, uint256)bytes32
epochState(address, uint256)uint8
feeRouter()address
finalityDepth()uint16
guardian()address
isClaimed(address, uint256, uint256)bool
lastOpenedEndBlock(address)uint64
lifetimeClaimed(address, address)uint256
nextEpochId(address)uint256
nonces(address, address)uint256
openLiability(address)uint256
pendingEpoch(address)uint256
proposer()address
remainingLiability(address, uint256)uint256
reservedFunding(address)uint256
reviewDelay()uint64
unallocatedFunding(address)uint256
weth()address

Events (8)

EpochAttestedEpochCancelledEpochEndCheckpointedEpochOpenedEpochProposedRewardClaimedRewardsFundedSupplementalEpochProposed

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "feeRouter_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "proposer_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "attester_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "guardian_",
        "type": "address"
      },
      {
        "internalType": "uint64",
        "name": "reviewDelay_",
        "type": "uint64"
      },
      {
        "internalType": "uint16",
        "name": "finalityDepth_",
        "type": "uint16"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AlreadyClaimed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "AuthorizationExpired",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "BalanceMismatch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientFunding",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidAmount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidAuthorization",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidBlockRange",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidCheckpoint",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidConfiguration",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidEpoch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidLeafCount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidProof",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidProof",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidState",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PendingProposal",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReviewDelay",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "RewardsLocked",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Unauthorized",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "claimsOpenAfter",
        "type": "uint256"
      }
    ],
    "name": "EpochAttested",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "restoredFunding",
        "type": "uint256"
      }
    ],
    "name": "EpochCancelled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "endBlock",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "blockHash",
        "type": "bytes32"
      }
    ],
    "name": "EpochEndCheckpointed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "liability",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "dust",
        "type": "uint256"
      }
    ],
    "name": "EpochOpened",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "rootHash",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "grossBudget",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "leafAggregate",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "leafCount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "bytes32",
        "name": "manifestHash",
        "type": "bytes32"
      }
    ],
    "name": "EpochProposed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "index",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "holder",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "destination",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "paidAsNative",
        "type": "bool"
      }
    ],
    "name": "RewardClaimed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "rewardAsset",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "RewardsFunded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "parentEpochId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "correctionId",
        "type": "uint256"
      }
    ],
    "name": "SupplementalEpochProposed",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "CLAIM_AUTHORIZATION_TYPEHASH",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DISTRIBUTOR_KIND",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DISTRIBUTOR_VERSION",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_LEAF_COUNT",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "REWARD_SINK_KIND",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "attestEpoch",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "attester",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "cancelEpoch",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "endBlock",
        "type": "uint256"
      }
    ],
    "name": "checkpointEpochEnd",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "hash",
        "type": "bytes32"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "endBlock",
        "type": "uint256"
      }
    ],
    "name": "checkpointedBlockHash",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "blockHash",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "index",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "holder",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "bool",
        "name": "asNative",
        "type": "bool"
      },
      {
        "components": [
          {
            "internalType": "bytes32[]",
            "name": "siblingHashes",
            "type": "bytes32[]"
          },
          {
            "internalType": "uint256[]",
            "name": "siblingSums",
            "type": "uint256[]"
          },
          {
            "internalType": "bytes32",
            "name": "boundaryLeafHash",
            "type": "bytes32"
          },
          {
            "internalType": "uint256",
            "name": "boundaryLeafAmount",
            "type": "uint256"
          },
          {
            "internalType": "bytes32[]",
            "name": "boundarySiblingHashes",
            "type": "bytes32[]"
          },
          {
            "internalType": "uint256[]",
            "name": "boundarySiblingSums",
            "type": "uint256[]"
          }
        ],
        "internalType": "struct AdvancedV3MerkleSum.Proof",
        "name": "proof",
        "type": "tuple"
      }
    ],
    "name": "claim",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "token",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "epochId",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "index",
            "type": "uint256"
          },
          {
            "internalType": "address",
            "name": "holder",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "amount",
            "type": "uint256"
          }
        ],
        "internalType": "struct AdvancedV3RewardDistributor.BatchClaim[]",
        "name": "claims",
        "type": "tuple[]"
      },
      {
        "components": [
          {
            "internalType": "bytes32[]",
            "name": "siblingHashes",
            "type": "bytes32[]"
          },
          {
            "internalType": "uint256[]",
            "name": "siblingSums",
            "type": "uint256[]"
          },
          {
            "internalType": "bytes32",
            "name": "boundaryLeafHash",
            "type": "bytes32"
          },
          {
            "internalType": "uint256",
            "name": "boundaryLeafAmount",
            "type": "uint256"
          },
          {
            "internalType": "bytes32[]",
            "name": "boundarySiblingHashes",
            "type": "bytes32[]"
          },
          {
            "internalType": "uint256[]",
            "name": "boundarySiblingSums",
            "type": "uint256[]"
          }
        ],
        "internalType": "struct AdvancedV3MerkleSum.Proof[]",
        "name": "proofs",
        "type": "tuple[]"
      }
    ],
    "name": "claimBatch",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "token",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "epochId",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "index",
            "type": "uint256"
          },
          {
            "internalType": "address",
            "name": "holder",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "amount",
            "type": "uint256"
          },
          {
            "internalType": "address",
            "name": "destination",
            "type": "address"
          },
          {
            "internalType": "bool",
            "name": "asNative",
            "type": "bool"
          },
          {
            "internalType": "uint256",
            "name": "nonce",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          }
        ],
        "internalType": "struct AdvancedV3RewardDistributor.DelegatedClaim",
        "name": "authorization",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "signature",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "bytes32[]",
            "name": "siblingHashes",
            "type": "bytes32[]"
          },
          {
            "internalType": "uint256[]",
            "name": "siblingSums",
            "type": "uint256[]"
          },
          {
            "internalType": "bytes32",
            "name": "boundaryLeafHash",
            "type": "bytes32"
          },
          {
            "internalType": "uint256",
            "name": "boundaryLeafAmount",
            "type": "uint256"
          },
          {
            "internalType": "bytes32[]",
            "name": "boundarySiblingHashes",
            "type": "bytes32[]"
          },
          {
            "internalType": "uint256[]",
            "name": "boundarySiblingSums",
            "type": "uint256[]"
          }
        ],
        "internalType": "struct AdvancedV3MerkleSum.Proof",
        "name": "proof",
        "type": "tuple"
      }
    ],
    "name": "claimDelegated",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "parentEpochId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "correctionId",
        "type": "uint256"
      }
    ],
    "name": "correctionIdUsed",
    "outputs": [
      {
        "internalType": "bool",
        "name": "used",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "destinationKind",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "epochCorrection",
    "outputs": [
      {
        "internalType": "bool",
        "name": "supplemental",
        "type": "bool"
      },
      {
        "internalType": "uint256",
        "name": "parentEpochId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "correctionId",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "epochFunding",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "grossBudget",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "remaining",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "attestedAt",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "openedAt",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "epochRange",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "startBlock",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "endBlock",
        "type": "uint256"
      },
      {
        "internalType": "bytes32",
        "name": "endBlockHash",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "epochRoot",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "rootHash",
        "type": "bytes32"
      },
      {
        "internalType": "uint256",
        "name": "leafAggregate",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "leafCount",
        "type": "uint256"
      },
      {
        "internalType": "bytes32",
        "name": "manifestHash",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "rewardAsset",
        "type": "address"
      },
      {
        "internalType": "bytes32",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "epochRootCommitment",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "epochState",
    "outputs": [
      {
        "internalType": "enum AdvancedV3RewardDistributor.EpochState",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "feeRouter",
    "outputs": [
      {
        "internalType": "contract IAdvancedV3RewardFeeRouter",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "finalityDepth",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "fund",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "guardian",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "index",
        "type": "uint256"
      }
    ],
    "name": "isClaimed",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "lastOpenedEndBlock",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "endBlock",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "holder",
        "type": "address"
      }
    ],
    "name": "lifetimeClaimed",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "nextEpochId",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "id",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "holder",
        "type": "address"
      }
    ],
    "name": "nonces",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "openClaims",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "openLiability",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "pendingEpoch",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "idPlusOne",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "epochId",
            "type": "uint256"
          },
          {
            "internalType": "uint64",
            "name": "startBlock",
            "type": "uint64"
          },
          {
            "internalType": "uint64",
            "name": "endBlock",
            "type": "uint64"
          },
          {
            "internalType": "bytes32",
            "name": "endBlockHash",
            "type": "bytes32"
          },
          {
            "internalType": "uint256",
            "name": "grossBudget",
            "type": "uint256"
          },
          {
            "internalType": "bytes32",
            "name": "rootHash",
            "type": "bytes32"
          },
          {
            "internalType": "uint256",
            "name": "leafAggregate",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "leafCount",
            "type": "uint256"
          },
          {
            "internalType": "bytes32",
            "name": "manifestHash",
            "type": "bytes32"
          }
        ],
        "internalType": "struct AdvancedV3RewardDistributor.Proposal",
        "name": "proposal",
        "type": "tuple"
      }
    ],
    "name": "proposeEpoch",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "parentEpochId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "correctionId",
        "type": "uint256"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "epochId",
            "type": "uint256"
          },
          {
            "internalType": "uint64",
            "name": "startBlock",
            "type": "uint64"
          },
          {
            "internalType": "uint64",
            "name": "endBlock",
            "type": "uint64"
          },
          {
            "internalType": "bytes32",
            "name": "endBlockHash",
            "type": "bytes32"
          },
          {
            "internalType": "uint256",
            "name": "grossBudget",
            "type": "uint256"
          },
          {
            "internalType": "bytes32",
            "name": "rootHash",
            "type": "bytes32"
          },
          {
            "internalType": "uint256",
            "name": "leafAggregate",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "leafCount",
            "type": "uint256"
          },
          {
            "internalType": "bytes32",
            "name": "manifestHash",
            "type": "bytes32"
          }
        ],
        "internalType": "struct AdvancedV3RewardDistributor.Proposal",
        "name": "proposal",
        "type": "tuple"
      }
    ],
    "name": "proposeSupplementalEpoch",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "proposer",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "epochId",
        "type": "uint256"
      }
    ],
    "name": "remainingLiability",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "reservedFunding",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "reviewDelay",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "unallocatedFunding",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "weth",
    "outputs": [
      {
        "internalType": "contract IAdvancedV3RewardWeth",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {AdvancedV3MerkleSum} from "./AdvancedV3MerkleSum.sol";

interface IAdvancedV3RewardFeeRouter {
    function weth() external view returns (address);
    function rewardAsset(address token) external view returns (address);
    function rewardUnlockBlock(address token) external view returns (uint256);
    function registeredPool(address token) external view returns (bytes32);
    function releaseRewards(address token, uint256 amount) external;
}

interface IAdvancedV3RewardWeth is IERC20 {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

/// @notice Non-upgradeable reserve and claims engine for Advanced V3 rewards.
/// @dev Open liabilities have no expiry, pause, cancellation, sweep, or rescue path.
contract AdvancedV3RewardDistributor is ReentrancyGuard {
    using SafeERC20 for IERC20;

    enum EpochState {
        None,
        Proposed,
        Attested,
        ClaimsOpen,
        Cancelled
    }

    struct Epoch {
        bytes32 poolId;
        address rewardAsset;
        bytes32 rootHash;
        bytes32 endBlockHash;
        bytes32 manifestHash;
        uint64 startBlock;
        uint64 endBlock;
        uint64 attestedAt;
        uint64 openedAt;
        uint128 grossBudget;
        uint128 leafAggregate;
        uint128 remainingLiability;
        uint32 leafCount;
        uint256 correctionId;
        uint256 parentEpochId;
        bool supplemental;
        EpochState state;
    }

    struct Proposal {
        uint256 epochId;
        uint64 startBlock;
        uint64 endBlock;
        bytes32 endBlockHash;
        uint256 grossBudget;
        bytes32 rootHash;
        uint256 leafAggregate;
        uint256 leafCount;
        bytes32 manifestHash;
    }

    struct DelegatedClaim {
        address token;
        uint256 epochId;
        uint256 index;
        address holder;
        uint256 amount;
        address destination;
        bool asNative;
        uint256 nonce;
        uint256 deadline;
    }

    struct ClaimInput {
        address token;
        uint256 epochId;
        uint256 index;
        address holder;
        uint256 amount;
        address destination;
        bool asNative;
    }

    struct BatchClaim {
        address token;
        uint256 epochId;
        uint256 index;
        address holder;
        uint256 amount;
    }

    bytes32 public constant REWARD_SINK_KIND = keccak256("COINBARREL_V3_REWARD_SINK");
    bytes32 public constant DISTRIBUTOR_KIND = keccak256("COINBARREL_ADVANCED_V3_REWARD_DISTRIBUTOR");
    uint16 public constant DISTRIBUTOR_VERSION = 1;
    uint256 public constant MAX_LEAF_COUNT = 1 << 20;
    bytes32 private constant EIP712_DOMAIN_TYPEHASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
    bytes32 private constant NAME_HASH = keccak256("Coinbarrel Advanced V3 Rewards");
    bytes32 private constant VERSION_HASH = keccak256("1");
    bytes32 public constant CLAIM_AUTHORIZATION_TYPEHASH = keccak256(
        "ClaimAuthorization(address token,address rewardAsset,uint256 epochId,bytes32 root,uint256 index,address holder,uint256 amount,address destination,bool asNative,uint256 nonce,uint256 deadline)"
    );
    uint256 private constant NATIVE_PAYOUT_GAS = 30_000;

    IAdvancedV3RewardFeeRouter public feeRouter;
    IAdvancedV3RewardWeth public weth;
    address public proposer;
    address public attester;
    address public guardian;
    uint64 public reviewDelay;
    uint16 public finalityDepth;

    mapping(address token => uint256 amount) public unallocatedFunding;
    mapping(address token => uint256 amount) public reservedFunding;
    mapping(address token => uint256 amount) public openLiability;
    mapping(address token => uint256 id) public nextEpochId;
    mapping(address token => uint256 idPlusOne) public pendingEpoch;
    mapping(address token => uint64 endBlock) public lastOpenedEndBlock;
    mapping(address token => mapping(uint256 epochId => Epoch)) private _epochs;
    mapping(address token => mapping(uint256 epochId => mapping(uint256 word => uint256 bitmap))) private _claimed;
    mapping(uint256 endBlock => bytes32 blockHash) public checkpointedBlockHash;
    mapping(address token => mapping(uint256 parentEpochId => mapping(uint256 correctionId => bool used))) public
        correctionIdUsed;
    mapping(address token => mapping(address holder => uint256 amount)) public lifetimeClaimed;
    mapping(address token => mapping(address holder => uint256 nonce)) public nonces;

    event RewardsFunded(address indexed token, address indexed rewardAsset, uint256 amount);
    event EpochEndCheckpointed(uint256 indexed endBlock, bytes32 indexed blockHash);
    event EpochProposed(
        address indexed token,
        uint256 indexed epochId,
        bytes32 indexed rootHash,
        uint256 grossBudget,
        uint256 leafAggregate,
        uint256 leafCount,
        bytes32 manifestHash
    );
    event EpochAttested(address indexed token, uint256 indexed epochId, uint256 claimsOpenAfter);
    event EpochOpened(address indexed token, uint256 indexed epochId, uint256 liability, uint256 dust);
    event EpochCancelled(address indexed token, uint256 indexed epochId, uint256 restoredFunding);
    event SupplementalEpochProposed(
        address indexed token, uint256 indexed epochId, uint256 indexed parentEpochId, uint256 correctionId
    );
    event RewardClaimed(
        address indexed token,
        uint256 indexed epochId,
        uint256 indexed index,
        address holder,
        address destination,
        uint256 amount,
        bool paidAsNative
    );

    error ZeroAddress();
    error InvalidConfiguration();
    error Unauthorized();
    error InvalidAmount();
    error InvalidEpoch();
    error InvalidState();
    error PendingProposal();
    error InsufficientFunding();
    error InvalidBlockRange();
    error InvalidCheckpoint();
    error ReviewDelay();
    error RewardsLocked();
    error AlreadyClaimed();
    error InvalidProof();
    error InvalidAuthorization();
    error AuthorizationExpired();
    error BalanceMismatch();

    constructor(
        address feeRouter_,
        address proposer_,
        address attester_,
        address guardian_,
        uint64 reviewDelay_,
        uint16 finalityDepth_
    ) {
        if (feeRouter_ == address(0) || proposer_ == address(0) || attester_ == address(0) || guardian_ == address(0)) revert ZeroAddress();
        if (reviewDelay_ == 0 || finalityDepth_ == 0 || finalityDepth_ >= 256) revert InvalidConfiguration();
        feeRouter = IAdvancedV3RewardFeeRouter(feeRouter_);
        weth = IAdvancedV3RewardWeth(IAdvancedV3RewardFeeRouter(feeRouter_).weth());
        proposer = proposer_;
        attester = attester_;
        guardian = guardian_;
        reviewDelay = reviewDelay_;
        finalityDepth = finalityDepth_;
    }

    function destinationKind() external pure returns (bytes32) {
        return REWARD_SINK_KIND;
    }

    function checkpointEpochEnd(uint256 endBlock) external returns (bytes32 hash) {
        if (
            endBlock >= block.number || block.number - endBlock < finalityDepth || block.number - endBlock > 256
                || checkpointedBlockHash[endBlock] != bytes32(0)
        ) revert InvalidCheckpoint();
        hash = blockhash(endBlock);
        if (hash == bytes32(0)) revert InvalidCheckpoint();
        checkpointedBlockHash[endBlock] = hash;
        emit EpochEndCheckpointed(endBlock, hash);
    }

    function fund(address token, uint256 amount) external nonReentrant {
        if (amount == 0) revert InvalidAmount();
        address asset = feeRouter.rewardAsset(token);
        uint256 beforeBalance = IERC20(asset).balanceOf(address(this));
        feeRouter.releaseRewards(token, amount);
        if (IERC20(asset).balanceOf(address(this)) != beforeBalance + amount) revert BalanceMismatch();
        unallocatedFunding[token] += amount;
        emit RewardsFunded(token, asset, amount);
    }

    function proposeEpoch(address token, Proposal calldata proposal) external {
        if (msg.sender != proposer) revert Unauthorized();
        uint64 previousEnd = lastOpenedEndBlock[token];
        if (
            proposal.startBlock >= proposal.endBlock
                || (previousEnd == 0 ? proposal.startBlock == 0 : proposal.startBlock != previousEnd)
                || proposal.endBlockHash == bytes32(0)
                || checkpointedBlockHash[proposal.endBlock] != proposal.endBlockHash
        ) revert InvalidBlockRange();
        _proposeEpoch(token, proposal, false, 0, 0);
    }

    function proposeSupplementalEpoch(
        address token,
        uint256 parentEpochId,
        uint256 correctionId,
        Proposal calldata proposal
    ) external {
        if (msg.sender != proposer) revert Unauthorized();
        Epoch storage parent = _epochs[token][parentEpochId];
        if (parent.state != EpochState.ClaimsOpen || parent.supplemental) revert InvalidEpoch();
        if (
            proposal.startBlock != parent.startBlock || proposal.endBlock != parent.endBlock
                || proposal.endBlockHash != parent.endBlockHash
        ) revert InvalidBlockRange();
        if (correctionIdUsed[token][parentEpochId][correctionId]) revert InvalidEpoch();
        correctionIdUsed[token][parentEpochId][correctionId] = true;
        _proposeEpoch(token, proposal, true, parentEpochId, correctionId);
        emit SupplementalEpochProposed(token, proposal.epochId, parentEpochId, correctionId);
    }

    function _proposeEpoch(
        address token,
        Proposal calldata proposal,
        bool supplemental,
        uint256 parentEpochId,
        uint256 correctionId
    ) private {
        if (pendingEpoch[token] != 0) revert PendingProposal();
        if (proposal.epochId != nextEpochId[token]) revert InvalidEpoch();
        if (
            proposal.grossBudget == 0 || proposal.leafAggregate == 0 || proposal.leafAggregate > proposal.grossBudget
                || proposal.grossBudget > type(uint128).max || proposal.leafAggregate > type(uint128).max
                || proposal.leafCount == 0 || proposal.leafCount > MAX_LEAF_COUNT || proposal.rootHash == bytes32(0)
                || proposal.manifestHash == bytes32(0)
        ) revert InvalidAmount();
        if (proposal.grossBudget > unallocatedFunding[token]) revert InsufficientFunding();

        bytes32 poolId = feeRouter.registeredPool(token);
        address asset = feeRouter.rewardAsset(token);
        unallocatedFunding[token] -= proposal.grossBudget;
        reservedFunding[token] += proposal.grossBudget;
        _epochs[token][proposal.epochId] = Epoch({
            poolId: poolId,
            rewardAsset: asset,
            rootHash: proposal.rootHash,
            endBlockHash: proposal.endBlockHash,
            manifestHash: proposal.manifestHash,
            startBlock: proposal.startBlock,
            endBlock: proposal.endBlock,
            attestedAt: 0,
            openedAt: 0,
            grossBudget: uint128(proposal.grossBudget),
            leafAggregate: uint128(proposal.leafAggregate),
            remainingLiability: 0,
            leafCount: uint32(proposal.leafCount),
            correctionId: correctionId,
            parentEpochId: parentEpochId,
            supplemental: supplemental,
            state: EpochState.Proposed
        });
        pendingEpoch[token] = proposal.epochId + 1;
        nextEpochId[token] = proposal.epochId + 1;
        emit EpochProposed(
            token,
            proposal.epochId,
            proposal.rootHash,
            proposal.grossBudget,
            proposal.leafAggregate,
            proposal.leafCount,
            proposal.manifestHash
        );
    }

    function attestEpoch(address token, uint256 epochId) external {
        if (msg.sender != attester) revert Unauthorized();
        Epoch storage epoch = _epochs[token][epochId];
        if (epoch.state != EpochState.Proposed || pendingEpoch[token] != epochId + 1) revert InvalidState();
        epoch.state = EpochState.Attested;
        epoch.attestedAt = uint64(block.timestamp);
        emit EpochAttested(token, epochId, block.timestamp + reviewDelay);
    }

    function openClaims(address token, uint256 epochId) external {
        Epoch storage epoch = _epochs[token][epochId];
        if (epoch.state != EpochState.Attested || pendingEpoch[token] != epochId + 1) revert InvalidState();
        if (block.timestamp < uint256(epoch.attestedAt) + reviewDelay) revert ReviewDelay();
        if (epoch.rewardAsset == token && block.number < feeRouter.rewardUnlockBlock(token)) revert RewardsLocked();
        uint256 dust = uint256(epoch.grossBudget) - epoch.leafAggregate;
        reservedFunding[token] -= epoch.grossBudget;
        unallocatedFunding[token] += dust;
        openLiability[token] += epoch.leafAggregate;
        epoch.remainingLiability = epoch.leafAggregate;
        epoch.openedAt = uint64(block.timestamp);
        epoch.state = EpochState.ClaimsOpen;
        if (!epoch.supplemental) lastOpenedEndBlock[token] = epoch.endBlock;
        pendingEpoch[token] = 0;
        emit EpochOpened(token, epochId, epoch.leafAggregate, dust);
    }

    function cancelEpoch(address token, uint256 epochId) external {
        if (msg.sender != guardian) revert Unauthorized();
        Epoch storage epoch = _epochs[token][epochId];
        if (
            (epoch.state != EpochState.Proposed && epoch.state != EpochState.Attested)
                || pendingEpoch[token] != epochId + 1
        ) revert InvalidState();
        epoch.state = EpochState.Cancelled;
        reservedFunding[token] -= epoch.grossBudget;
        unallocatedFunding[token] += epoch.grossBudget;
        pendingEpoch[token] = 0;
        emit EpochCancelled(token, epochId, epoch.grossBudget);
    }

    function claim(
        address token,
        uint256 epochId,
        uint256 index,
        address holder,
        uint256 amount,
        bool asNative,
        AdvancedV3MerkleSum.Proof calldata proof
    ) external nonReentrant {
        bool holderSelectedMode = msg.sender == holder;
        _claim(
            ClaimInput({
                token: token,
                epochId: epochId,
                index: index,
                holder: holder,
                amount: amount,
                destination: holder,
                asNative: asNative && holderSelectedMode
            }),
            proof
        );
    }

    function claimDelegated(
        DelegatedClaim calldata authorization,
        bytes calldata signature,
        AdvancedV3MerkleSum.Proof calldata proof
    ) external nonReentrant {
        if (authorization.destination == address(0)) revert ZeroAddress();
        if (block.timestamp > authorization.deadline) revert AuthorizationExpired();
        Epoch storage epoch = _epochs[authorization.token][authorization.epochId];
        if (authorization.nonce != nonces[authorization.token][authorization.holder]) revert InvalidAuthorization();
        if (!SignatureChecker.isValidSignatureNow(
                authorization.holder, _authorizationDigest(authorization, epoch.rewardAsset, epoch.rootHash), signature
            )) revert InvalidAuthorization();
        nonces[authorization.token][authorization.holder] = authorization.nonce + 1;
        _claim(
            ClaimInput({
                token: authorization.token,
                epochId: authorization.epochId,
                index: authorization.index,
                holder: authorization.holder,
                amount: authorization.amount,
                destination: authorization.destination,
                asNative: authorization.asNative
            }),
            proof
        );
    }

    /// @notice Permissionlessly submits multiple claims, always paying each holder in the ERC-20 reward asset.
    function claimBatch(BatchClaim[] calldata claims, AdvancedV3MerkleSum.Proof[] calldata proofs)
        external
        nonReentrant
    {
        if (claims.length == 0 || claims.length != proofs.length) revert InvalidAmount();
        for (uint256 i; i < claims.length; ++i) {
            BatchClaim calldata item = claims[i];
            _claim(
                ClaimInput({
                    token: item.token,
                    epochId: item.epochId,
                    index: item.index,
                    holder: item.holder,
                    amount: item.amount,
                    destination: item.holder,
                    asNative: false
                }),
                proofs[i]
            );
        }
    }

    function isClaimed(address token, uint256 epochId, uint256 index) public view returns (bool) {
        return (_claimed[token][epochId][index >> 8] & (uint256(1) << (index & 255))) != 0;
    }

    function epochRootCommitment(address token, uint256 epochId) external view returns (bytes32) {
        Epoch storage epoch = _epochs[token][epochId];
        if (epoch.state == EpochState.None) revert InvalidEpoch();
        return AdvancedV3MerkleSum.rootCommitment(
            AdvancedV3MerkleSum.Root({hash: epoch.rootHash, sum: epoch.leafAggregate, leafCount: epoch.leafCount})
        );
    }

    function remainingLiability(address token, uint256 epochId) external view returns (uint256) {
        return _epochs[token][epochId].remainingLiability;
    }

    function epochState(address token, uint256 epochId) external view returns (EpochState) {
        return _epochs[token][epochId].state;
    }

    function epochCorrection(address token, uint256 epochId)
        external
        view
        returns (bool supplemental, uint256 parentEpochId, uint256 correctionId)
    {
        Epoch storage epoch = _epochs[token][epochId];
        if (epoch.state == EpochState.None) revert InvalidEpoch();
        return (epoch.supplemental, epoch.parentEpochId, epoch.correctionId);
    }

    function epochRange(address token, uint256 epochId)
        external
        view
        returns (uint256 startBlock, uint256 endBlock, bytes32 endBlockHash)
    {
        Epoch storage epoch = _epochs[token][epochId];
        if (epoch.state == EpochState.None) revert InvalidEpoch();
        return (epoch.startBlock, epoch.endBlock, epoch.endBlockHash);
    }

    function epochRoot(address token, uint256 epochId)
        external
        view
        returns (
            bytes32 rootHash,
            uint256 leafAggregate,
            uint256 leafCount,
            bytes32 manifestHash,
            address rewardAsset,
            bytes32 poolId
        )
    {
        Epoch storage epoch = _epochs[token][epochId];
        if (epoch.state == EpochState.None) revert InvalidEpoch();
        return
            (epoch.rootHash, epoch.leafAggregate, epoch.leafCount, epoch.manifestHash, epoch.rewardAsset, epoch.poolId);
    }

    function epochFunding(address token, uint256 epochId)
        external
        view
        returns (uint256 grossBudget, uint256 remaining, uint256 attestedAt, uint256 openedAt)
    {
        Epoch storage epoch = _epochs[token][epochId];
        if (epoch.state == EpochState.None) revert InvalidEpoch();
        return (epoch.grossBudget, epoch.remainingLiability, epoch.attestedAt, epoch.openedAt);
    }

    function _claim(ClaimInput memory input, AdvancedV3MerkleSum.Proof calldata proof) private {
        if (input.holder == address(0) || input.destination == address(0) || input.amount == 0) {
            revert InvalidAmount();
        }
        Epoch storage epoch = _epochs[input.token][input.epochId];
        if (epoch.state != EpochState.ClaimsOpen) revert InvalidState();
        if (isClaimed(input.token, input.epochId, input.index)) revert AlreadyClaimed();
        bytes32 leaf = _leafHash(input.token, input.epochId, input.index, input.holder, input.amount, epoch);
        bool valid = AdvancedV3MerkleSum.verify(
            AdvancedV3MerkleSum.Root({hash: epoch.rootHash, sum: epoch.leafAggregate, leafCount: epoch.leafCount}),
            input.index,
            leaf,
            input.amount,
            proof
        );
        if (!valid || input.amount > epoch.remainingLiability) revert InvalidProof();

        _claimed[input.token][input.epochId][input.index >> 8] |= uint256(1) << (input.index & 255);
        epoch.remainingLiability -= uint128(input.amount);
        openLiability[input.token] -= input.amount;
        lifetimeClaimed[input.token][input.holder] += input.amount;
        bool paidAsNative = _pay(epoch.rewardAsset, input.destination, input.amount, input.asNative);
        emit RewardClaimed(
            input.token, input.epochId, input.index, input.holder, input.destination, input.amount, paidAsNative
        );
    }

    function _pay(address asset, address destination, uint256 amount, bool asNative)
        private
        returns (bool paidAsNative)
    {
        if (asset == address(weth) && asNative) {
            weth.withdraw(amount);
            (paidAsNative,) = payable(destination).call{value: amount, gas: NATIVE_PAYOUT_GAS}("");
            if (!paidAsNative) {
                weth.deposit{value: amount}();
                IERC20(asset).safeTransfer(destination, amount);
            }
        } else {
            IERC20(asset).safeTransfer(destination, amount);
        }
    }

    function _authorizationDigest(DelegatedClaim calldata authorization, address rewardAsset_, bytes32 root)
        private
        view
        returns (bytes32)
    {
        return _hashTypedData(
            keccak256(
                abi.encode(
                    CLAIM_AUTHORIZATION_TYPEHASH,
                    authorization.token,
                    rewardAsset_,
                    authorization.epochId,
                    root,
                    authorization.index,
                    authorization.holder,
                    authorization.amount,
                    authorization.destination,
                    authorization.asNative,
                    authorization.nonce,
                    authorization.deadline
                )
            )
        );
    }

    function _hashTypedData(bytes32 structHash) private view returns (bytes32) {
        bytes32 domainSeparator =
            keccak256(abi.encode(EIP712_DOMAIN_TYPEHASH, NAME_HASH, VERSION_HASH, block.chainid, address(this)));
        return keccak256(abi.encodePacked(hex"1901", domainSeparator, structHash));
    }

    function _leafHash(
        address token,
        uint256 epochId,
        uint256 index,
        address holder,
        uint256 amount,
        Epoch storage epoch
    ) private view returns (bytes32) {
        return AdvancedV3MerkleSum.leafHash(
            block.chainid, address(this), token, epoch.poolId, epoch.rewardAsset, epochId, index, holder, amount
        );
    }

    receive() external payable {}
}
Chain explorer8527msChain node85ms