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
- Creator
- 0x30e4B6dc31…ca4f1CddBa
- 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