From 3eec97dd1c0e3b153c89fa3cd6334612679439c3 Mon Sep 17 00:00:00 2001 From: Michael Sun <35479365+8sunyuan@users.noreply.github.com> Date: Tue, 7 May 2024 12:04:18 -0400 Subject: [PATCH] feat: payments v1 (#514) * feat: payments w tests and deploy script (#512) * feat: payments w tests and deploy script * chore: remove activatedAt and add require checks payForRange startTimestamp is multiple of calculationIntervalSeconds * chore: uint32 for timestamps * fix: storage off by one * chore: additional natspec (#519) * feat: payments w tests and deploy script (#512) * feat: payments w tests and deploy script * chore: remove activatedAt and add require checks payForRange startTimestamp is multiple of calculationIntervalSeconds * chore: uint32 for timestamps * fix: storage off by one * chore: additional natspec * chore: requested changes and fixes * chore: pragmas * refactor: reuse state read and clean up logic * fix: relative paths * chore: update event and tests * fix: comments and add tests * test: paused submitRoot flag * chore: contract organization and added view fn * chore: require comment * chore: natspec * feat: token receiver addresses * chore: natspec with tokenReceivers * fix: integration tests * fix: integration tests * chore: constants and immutables * test: additional unit test for max index * chore: deployed new test contract * chore: update deployment address * chore: add single recipient addr * chore: upgrade payment impl * chore: upgrade payment impl * feat: ipfs hashes in distributionRoot * fix: use CID instead of hash * chore: remove ipfs * docs: missing natspec param * feat: update script to deploy/upgrade * chore: make calc interval secs to immutable * fix: add required interfaces * docs: paymentCoordinator docs (#527) * docs: paymentCoord docs * docs: clean up payment docs * docs: tree structure png * fix comment * docs: update single recipient addr * docs: tweak payment docs for clarity and brevity --------- Co-authored-by: wadealexc * refactor: small refactor for readability to surface token transfer and isolate input validation (#530) * fix payment deploy * fix: allow verifyInclusion for 0 len proof also make cumulativeEarnings check strictly gt * fix: preprod deploy * fix: merkle diagram and docs * feat: add payments off-chain assumptions (#533) * feat: add offchain assumptions to docs; Add offchain variables & token requirement * docs: simplify off-chain calculcation description * fix: doc ref * fix: heading typo * refactor: make offchain constants internal * docs: fix typos * test: single token/earner leaves (#537) --------- Co-authored-by: wadealexc Co-authored-by: Alex <18387287+wadealexc@users.noreply.github.com> Co-authored-by: gpsanant Co-authored-by: Yash Patil <40046473+ypatil12@users.noreply.github.com> --- docs/README.md | 14 + docs/core/PaymentCoordinator.md | 365 +++ .../images/PaymentCoordinator_Merkle_Tree.png | Bin 0 -> 271396 bytes ...loy_PaymentCoordinator.holesky.config.json | 58 + .../Holesky_current_deployment.config.json | 3 + .../Mainnet_current_deployment.config.json | 3 + .../Deploy_Preprod_PaymentCoordinator.s.sol | 53 + .../Deploy_Test_PaymentCoordinator.s.sol | 95 + ...loy_PaymentCoordinator.holesky.config.json | 37 + ...entCoordinator_Preprod.holesky.config.json | 37 + .../M2_deploy_from_scratch.output.json | 61 + .../holesky/M2_deploy_preprod.output.json | 39 + script/utils/ExistingDeploymentParser.sol | 187 +- src/contracts/core/PaymentCoordinator.sol | 526 +++++ .../core/PaymentCoordinatorStorage.sol | 115 + .../interfaces/IDelegationManager.sol | 3 + .../interfaces/IPaymentCoordinator.sol | 311 +++ src/contracts/interfaces/IStrategyManager.sol | 3 + src/contracts/libraries/Merkle.sol | 5 +- src/test/EigenPod.t.sol | 23 - src/test/events/IPaymentCoordinatorEvents.sol | 67 + src/test/mocks/DelegationManagerMock.sol | 3 + src/test/mocks/StrategyManagerMock.sol | 7 + ...ssClaimProofs_MaxEarnerAndLeafIndices.json | 24 + .../processClaimProofs_Root1.json | 54 + .../processClaimProofs_Root2.json | 54 + .../processClaimProofs_Root3.json | 54 + .../processClaimProofs_SingleEarnerLeaf.json | 36 + .../processClaimProofs_SingleTokenLeaf.json | 24 + .../processClaim_Preprod_Test.json | 24 + src/test/unit/PaymentCoordinatorUnit.t.sol | 2004 +++++++++++++++++ 31 files changed, 4235 insertions(+), 54 deletions(-) create mode 100644 docs/core/PaymentCoordinator.md create mode 100644 docs/images/PaymentCoordinator_Merkle_Tree.png create mode 100644 script/configs/holesky/Deploy_PaymentCoordinator.holesky.config.json create mode 100644 script/deploy/holesky/Deploy_Preprod_PaymentCoordinator.s.sol create mode 100644 script/deploy/holesky/Deploy_Test_PaymentCoordinator.s.sol create mode 100644 script/output/holesky/Deploy_PaymentCoordinator.holesky.config.json create mode 100644 script/output/holesky/Deploy_PaymentCoordinator_Preprod.holesky.config.json create mode 100644 script/output/holesky/M2_deploy_from_scratch.output.json create mode 100644 script/output/holesky/M2_deploy_preprod.output.json create mode 100644 src/contracts/core/PaymentCoordinator.sol create mode 100644 src/contracts/core/PaymentCoordinatorStorage.sol create mode 100644 src/contracts/interfaces/IPaymentCoordinator.sol create mode 100644 src/test/events/IPaymentCoordinatorEvents.sol create mode 100644 src/test/test-data/paymentCoordinator/processClaimProofs_MaxEarnerAndLeafIndices.json create mode 100644 src/test/test-data/paymentCoordinator/processClaimProofs_Root1.json create mode 100644 src/test/test-data/paymentCoordinator/processClaimProofs_Root2.json create mode 100644 src/test/test-data/paymentCoordinator/processClaimProofs_Root3.json create mode 100644 src/test/test-data/paymentCoordinator/processClaimProofs_SingleEarnerLeaf.json create mode 100644 src/test/test-data/paymentCoordinator/processClaimProofs_SingleTokenLeaf.json create mode 100644 src/test/test-data/paymentCoordinator/processClaim_Preprod_Test.json create mode 100644 src/test/unit/PaymentCoordinatorUnit.t.sol diff --git a/docs/README.md b/docs/README.md index d6791a119..579dc056d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,6 +12,7 @@ This document provides an overview of system components, contracts, and user rol * [`EigenPodManager`](#eigenpodmanager) * [`StrategyManager`](#strategymanager) * [`DelegationManager`](#delegationmanager) + * [`PaymentCoordinator`](#paymentcoordinator) * [`AVSDirectory`](#avsdirectory) * [`Slasher`](#slasher) * [Roles and Actors](#roles-and-actors) @@ -66,6 +67,19 @@ The `DelegationManager` sits between the `EigenPodManager` and `StrategyManager` See full documentation in [`/core/DelegationManager.md`](./core/DelegationManager.md). +#### PaymentCoordinator + +| File | Type | Proxy | +| -------- | -------- | -------- | +| [`PaymentCoordinator.sol`](../src/contracts/core/PaymentCoordinator.sol) | Singleton | Transparent proxy | + +The `PaymentCoordinator` is the main entry point of submission and claiming of ERC20 payments in EigenLayer. It carries out three basic functions: +* AVSs (via the AVS's contracts) submit "range payments" to their registered Operators and Stakers over a specific time period +* *Off-chain*, the payment updater will use each range payment's time period to apply payment amounts to historical Staker/Operator stake weights. This is consolidated into a merkle root that is posted *on-chain* to the `PaymentCoordinator`, allowing Stakers/Operators to claim their allocated payments. +* Stakers/Operators can claim payments posted by the payment updater. + +See full documentation in [`/core/PaymentCoordinator.md`](./core/PaymentCoordinator.md). + #### AVSDirectory | File | Type | Proxy | diff --git a/docs/core/PaymentCoordinator.md b/docs/core/PaymentCoordinator.md new file mode 100644 index 000000000..daa519698 --- /dev/null +++ b/docs/core/PaymentCoordinator.md @@ -0,0 +1,365 @@ +## PaymentCoordinator + +| File | Type | Proxy | +| -------- | -------- | -------- | +| [`PaymentCoordinator.sol`](../../src/contracts/core/PaymentCoordinator.sol) | Singleton | Transparent proxy | + + + + +The `PaymentCoordinator` accepts ERC20s from AVSs alongside payment requests made out to Operators who, during a specified time range, were registered to the AVS in the core [`AVSDirectory`](./AVSDirectory.md) contract. + +*Off-chain*, the trusted *payment updater* calculates a payment distribution according to (as of the payment's time range): (i) the relative stake weight of each Operator's Stakers and (ii) a globally-defined commission given to the Operator. *On-chain*, the payment updater sends the `PaymentCoordinator` a merkle root of each earner's cumulative earnings. + +Earners provide merkle proofs to the `PaymentCoordinator` to claim payments against these roots. + +The typical user flow is as follows: +1. An AVS submits a `RangePayment` to the `PaymentCoordinator` contract, which specifies a time range (`startTimestamp` and `duration`) and payment (`token` and `amount`). The `RangePayment` also specifies the strategies Stakers/Operators should have held over the time range in order to receive the payments (i.e. "pay out to holders of X strategy specifically"). +2. Off-chain, `RangePayments` are used to calculate payment distributions, which are periodically consolidated into a merkle tree. +3. The root of this tree (aka the `DistributionRoot`) is posted on-chain by the *payment updater*. A `DistributionRoot` becomes active for claims after some globally-configured `activationDelay`. +4. Stakers and Operators (or their configured "claimers") can claim their accumulated earnings by providing a merkle proof against any previously-posted `DistributionRoot`. + +This entire flow will repeat periodically as AVSs submit `RangePayments`, `DistributionRoots` are submitted, and Stakers/Operators claim their accumulated earnings. Note that `DistributionRoots` contain *cumulative earnings*, meaning Stakers/Operators aren't required to claim against every root - simply claiming against the most recent root will claim anything not yet claimed. + +#### High-level Concepts + +This document is organized according to the following themes (click each to be taken to the relevant section): +* [Submitting Payment Requests](#submitting-payment-requests) +* [Distributing and Claiming Payments](#distributing-and-claiming-payments) +* [System Configuration](#system-configuration) +* [Payments Merkle Tree Structure](#payments-merkle-tree-structure) +* [Off Chain Calculation](#off-chain-calculation) + +#### Important state variables + +* `DistributionRoot[] public distributionRoots`: + * `distributionRoots` stores historic payment merkle tree roots submitted by the payment updater. For each earner, the payment merkle tree stores cumulative earnings per ERC20 payment token. For more details on merkle tree structure see [Payment Merkle Tree Structure](#payments-merkle-tree-structure) below. +* `mapping(address => address) public claimerFor`: earner => claimer + * Stakers and Operators can designate a "claimer" who can claim payments via on their behalf via `processClaim`. If a claimer is not set in `claimerFor`, the earner will have to call `processClaim` themselves. + * Note that the claimer isn't necessarily the payment recipient, but they do have the authority to specify the recipient when calling `processClaim` on the earner's behalf. +* `mapping(address => mapping(IERC20 => uint256)) public cumulativeClaimed`: earner => token => total amount claimed to date + * Mapping for earners(Stakers/Operators) to track their total claimed earnings per payment token. This mapping is used to calculate the difference between the cumulativeEarnings stored in the merkle tree and the previous total claimed amount. This difference is then transfered to the specified destination address. +* `uint16 public globalOperatorCommissionBips`: *Used off-chain* by the payment updater to calculate an Operator's commission for a specific payment. + * This is expected to be a flat 10% rate for the initial payments release. Expressed in basis points, this is `1000`. + +#### Helpful definitions + +* `_checkClaim(PaymentMerkleClaim calldata claim, DistributionRoot memory root)` + * Checks the merkle inclusion of a claim against a `DistributionRoot` + * Reverts if any of the following are true: + * mismatch input param lengths: tokenIndices, tokenTreeProofs, tokenLeaves + * earner proof reverting from calling `_verifyEarnerClaimProof` + * any of the token proofs reverting from calling `_verifyTokenClaimProof` +* Wherever AVS (Actively Validated Service) is mentioned, it refers to the contract entity that is submitting payments to the `PaymentCoordinator`. This is assumed to be a customized `ServiceManager` contract of some kind that is interfacing with the EigenLayer protocol. See the `ServiceManagerBase` docs here: [`eigenlayer-middleware/docs/ServiceManagerBase.md`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/dev/docs/ServiceManagerBase.md). + +--- + +### Submitting Payment Requests + +AVSs submit payment requests via the following functions: + +* [`PaymentCoordinator.payForRange`](#payforrange) +* [`PaymentCoordinator.payAllForRange`](#payallforrange) + +#### `payForRange` + +```solidity +function payForRange( + RangePayment[] calldata rangePayments +) + external + onlyWhenNotPaused(PAUSED_PAY_FOR_RANGE) + nonReentrant +``` + +Called by an AVS to submit a list of `RangePayments` to be distributed across all registered Operators (and Stakers delegated to each Operator). A `RangePayment` consists of the following fields: +* `IERC20 token`: the address of the ERC20 token being used for payment +* `uint256 amount`: amount of `token` to transfer to the `PaymentCoordinator` +* `uint32 startTimestamp`: the start of the payment time range +* `uint32 duration`: the duration of the payment time range, in seconds +* `StrategyAndMultiplier[] strategiesAndMultipliers`: an array of `StrategyAndMultiplier` structs that define a linear combination of EigenLayer strategies the AVS is considering eligible for payment. Each `StrategyAndMultiplier` contains: + * `IStrategy strategy`: address of the strategy against which a Staker/Operator's relative shares are weighted in order to determine their payment amount + * `uint96 multiplier`: the relative weighting of the strategy in the linear combination. (Recommended use here is to use 1e18 as the base multiplier and adjust the relative weightings accordingly) + +For each submitted `RangePayment`, this method performs a `transferFrom` to transfer the specified payment `token` and `amount` from the caller to the `PaymentCoordinator`. + +*Eligibility*: + +In order to be eligible to claim a `payForRange` payment, the Operator should be registered for the AVS in the `AVSDirectory` during the time period over which the payment is being made (see docs for [`AVSDirectory.registerOperatorToAVS`](./AVSDirectory.md#registeroperatortoavs)). If an Operator is ineligible, any Stakers delegated to the Operator are also ineligible. + +*Payment Distribution*: + +The payment distribution amongst the AVS's Operators and delegated Stakers is determined offchain using the strategies and multipliers provided in the `RangePayment` struct as well as the actual shares for those defined strategies over the `RangePayment's` time range. These shares are read from the [`EigenPodManager`](./EigenPodManager.md) (in the case of the Beacon Chain ETH strategy), or the [`StrategyManager`](./StrategyManager.md) for any other strategy. Note that Stakers' shares specifically are what determines payment distribution; Operators earn based on a combination of their own deposited shares and a configured `globalOperatorCommissionBips`. + +*Effects*: +* For each `RangePayment` element + * Transfers `amount` of `token` from the msg.sender (AVS) to the `PaymentCoordinator` + * Hashes msg.sender(AVS), nonce, and `RangePayment` struct to create a unique payment hash and sets this value to `true` in the `isRangePaymentHash` mapping + * Increments `paymentNonce[msg.sender]` + * Emits a `RangePaymentCreated` event + +*Requirements*: +* Pause status MUST NOT be set: `PAUSED_PAY_FOR_RANGE` +* Function call is not reentered +* For each `RangePayment` element + * `isRangePaymentHash[msg.sender][rangePaymentHash]` MUST be false, the hash keccak256(msg.sender[AVS], nonce, and `RangePayment`) should not have been submitted before + * Requirements from calling internal function `_payForRange()` + * `rangePayment.strategiesAndMultipliers.length > 0` + * `rangePayment.amount > 0` + * `rangePayment.amount <= MAX_PAYMENT_AMOUNT` + * `rangePayment.duration <= MAX_PAYMENT_DURATION` + * `rangePayment.duration % calculationIntervalSeconds == 0` + * `rangePayment.startTimestamp % calculationIntervalSeconds == 0` + * `block.timestamp - MAX_RETROACTIVE_LENGTH <= rangePayment.startTimestamp` + * `GENESIS_PAYMENT_TIMESTAMP <= rangePayment.startTimestamp` + * `rangePayment.startTimestamp <= block.timestamp + MAX_FUTURE_LENGTH` + * Requirements for `rangePayment.strategiesAndMultipliers` + * Each `strategy` is whitelisted for deposit in the StrategyManager or is the `beaconChainETHStrategy` + * `rangePayment.strategiesAndMultipliers` is sorted by ascending strategy address to prevent duplicate strategies + * `transferFrom` MUST succeed in transferring `amount` of `token` from `msg.sender` to the `PaymentCoordinator` + +The text diagram below better visualizes a valid start timestamp for a `RangePayment` +``` +Sliding Window for valid RangePayment startTimestamp + +Scenario A: GENESIS_PAYMENT_TIMESTAMP IS WITHIN RANGE + <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH---> + <--------------------valid range for startTimestamp------------------------> + ^ + GENESIS_PAYMENT_TIMESTAMP + + +Scenario B: GENESIS_PAYMENT_TIMESTAMP IS OUT OF RANGE + <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH---> + <------------------------valid range for startTimestamp------------------------> + ^ +GENESIS_PAYMENT_TIMESTAMP +``` + +#### `payAllForRange` + +```solidity +function payAllForRange( + RangePayment[] calldata rangePayments +) + external + onlyWhenNotPaused(PAUSED_PAY_ALL_FOR_RANGE) + onlyPayAllForRangeSubmitter + nonReentrant +``` + +This method is identical in function to [`payForRange`](#payforrange) above, except: +* It can only be called by a whitelisted "pay all for range submitter" +* ALL Stakers/Operators are eligible for payment, instead of those specifically registered for a given AVS + +*Effects*: +* See [`payForRange`](#payforrange) above. The only differences are that: + * Each range payment hash is stored in the `isRangePaymentForAllHash` mapping + * Emits a `RangePaymentForAllCreated` event + +*Requirements*: +* See [`payForRange`](#payforrange) above. The only difference is that each calculated range payment hash MUST NOT already exist in the `isRangePaymentForAllHash` mapping. + +--- + +### Distributing and Claiming Payments + +The *payment updater* calculates payment distributions and submit claimable roots through the following function: + +* [`PaymentCoordinator.submitRoot`](#submitroot) + +Earners configure and claim these payments using the following functions: + +* [`PaymentCoordinator.setClaimerFor`](#setclaimerfor) +* [`PaymentCoordinator.processClaim`](#processclaim) + +#### `submitRoot` + +```solidity +function submitRoot( + bytes32 root, + uint32 paymentCalculationEndTimestamp +) + external + onlyWhenNotPaused(PAUSED_SUBMIT_ROOTS) + onlyPaymentUpdater +``` + +Called only by the `paymentUpdater` address to create a new `DistributionRoot` in the PaymentCoordinator. The `DistributionRoot` struct contains the following fields: +* `bytes32 root`: the merkle root of the payment merkle tree +* `uint32 paymentCalculationEndTimestamp`: the end of the payment time range for which the `DistributionRoot` is being submitted +* `uint32 activatedAt`: the timestamp in seconds when the `DistributionRoot` is activated and can be claimed against + +`submitRoot` pushes a new `DistributionRoot` to the `distributionRoots` array. The `DistributionRoot.activatedAt` timestamp is set to `block.timestamp + activationDelay()` to allow for a delay before claims can be processed. Once this delay has passed, the root can be used to verify merkle proofs of payments made out to Stakers/Operators. + +*Effects*: +* Pushes a new `DistributionRoot` to the `distributionRoots` array +* Sets `currPaymentCalculationEndTimestamp` to the param `paymentCalculationEndTimestamp` +* Emits a `DistributionRootSubmitted` event + +*Requirements*: +* Pause status MUST NOT be set: `PAUSED_SUBMIT_ROOTS` +* `msg.sender` MUST be the `paymentUpdater` +* `paymentCalculationEndTimestamp > currPaymentCalculationEndTimestamp` +* `paymentCalculationEndTimestamp < block.timestamp` + +#### `setClaimerFor` + +```solidity +function setClaimerFor(address claimer) external +``` + +Called by an earner (Staker/Operator) to set a claimer address that can call `processClaim` on their behalf. If the claimer is not set (`claimerFor[earner] == address(0)`), the earner themselves can call `processClaim` directly. + +*Effects*: +* Sets the `claimerFor[msg.sender]` to the input param `claimer` +* Emits a `ClaimerForSet` event + +#### `processClaim` + +```solidity +function processClaim( + PaymentMerkleClaim calldata claim, + address recipient +) + external + onlyWhenNotPaused(PAUSED_CLAIM_PAYMENTS) + nonReentrant +``` + +Called an earner (Staker/Operator) to claim their accumulated earnings by providing a merkle proof against a posted `DistributionRoot`. If the earner has configured a claimer (via `setClaimerFor`), the claimer must call this method instead. + +The `PaymentMerkleClaim` struct contains the following fields (see [Payments Merkle Tree Structure](#payments-merkle-tree-structure) for further details): +* `uint32 rootIndex`: the index of the `DistributionRoot` in `distributionRoots` to prove against +* `uint32 earnerIndex`: the index of the earner's account root in the merkle tree +* `bytes earnerTreeProof`: the proof of the earner's `EarnerTreeMerkleLeaf` against the `DistributionRoot` +* `EarnerTreeMerkleLeaf earnerLeaf`: the earner's address and token subtree root + * `address earner`: the address of the earner + * `bytes32 earnerTokenRoot`: the merkle root of the earner's token merkle tree +* `uint32[] tokenIndices`: the indices of the token leaves in the earner's subtree +* `bytes[] tokenTreeProofs`: the proofs of the token leaves against the earner's `earnerTokenRoot` +* `TokenTreeMerkleLeaf[] tokenLeaves`: the token leaves to be claimed: + * `IERC20 token`: the ERC20 token to be claimed + * `uint256 amount`: the amount of the ERC20 token to be claimed + +`processClaim` will first call `_checkClaim` to verify the merkle proofs against the `DistributionRoot` at the specified `rootIndex`. This is done by first performing a merkle proof verification of the earner's `EarnerTreeMerkleLeaf` against the `DistributionRoot` and then for each tokenIndex, verifying each token leaf against the earner's `earnerTokenRoot`. + +The caller must be the set claimer address in the `claimerFor` mapping or the earner themselves if the claimer is not set. + +After the claim is verified, for each token leaf, the difference between the cumulative earnings in the merkle tree and the previous total claimed amount last stored in the contract is calculated and transferred from the `PaymentCoordinator` contract to the address `recipient`. + +*Effects*: +* For each `claim.tokenLeaves`: + * Calculates `uint claimAmount = tokenLeaf.cumulativeEarnings - cumulativeClaimed[earner][tokenLeaf.token]` + * Transfers `claimAmount` of `tokenLeaf.token` to the specified `recipient` + * Updates the `cumulativeClaimed` mapping for the earner and token + * Emits a `PaymentClaimed` event + +*Requirements*: +* Pause status MUST NOT be set: `PAUSED_CLAIM_PAYMENTS` +* The `claim` must have valid proofs against a valid `DistributionRoot`: + * For the `DistributionRoot` given by `claim.rootIndex`, the root MUST be active (`block.timestamp >= root.activatedAt`) + * `claim.tokenIndices` MUST equal the lengths of `claim.TokenTreeProofs` AND `claim.tokenLeaves` + * `claim.earnerTreeProof` MUST validate `claim.earnerLeaf` against the `DistributionRoot` + * For each `claim.tokenIndices[i]`: + * `claim.tokenTreeProofs[i]` MUST validate `claim.tokenLeaves[i]` against `claim.earnerLeaf.earnerTokenRoot` +* If the `earner` specified in `claim.earnerLeaf.earner` has a designated `claimer` in `claimerFor[earner]`, `msg.sender` MUST be the `claimer` + * Otherwise, `msg.sender` MUST be the `earner` +* For each `TokenTreeMerkleLeaf`, + * `tokenLeaf.cumulativeEarnings > cumulativeClaimed[earner][token]`: cumulativeEarnings must be gt than cumulativeClaimed. Trying to reclaim with the same proofs will revert because the claimed and earnings values will equal, breaking this requirement. + * `tokenLeaf.token.safeTransfer(recipient, claimAmount)` MUST succeed + +--- + +### System Configuration + +* [`PaymentCoordinator.setActivationDelay`](#setactivationdelay) +* [`PaymentCoordinator.setGlobalOperatorCommission`](#setglobaloperatorcommission) +* [`PaymentCoordinator.setPaymentUpdater`](#setpaymentupdater) +* [`PaymentCoordinator.setPayAllForRangeSubmitter`](#setpayallforrangesubmitter) + +#### `setActivationDelay` + +```solidity +function setActivationDelay(uint32 _activationDelay) external onlyOwner +``` + +Allows the Owner to set the global `activationDelay`. The activation delay is the time in seconds after a `DistributionRoot` is submitted before it can be claimed against. This delay is to allow for interested parties to perform verification of the root before claiming begins. + +*Effects*: +* Sets the global `activationDelay` +* Emits a `ActivationDelaySet` event + +*Requirements*: +* Caller MUST be the Owner + +#### `setGlobalOperatorCommission` + +```solidity +function setGlobalOperatorCommission(uint16 _globalCommissionBips) external onlyOwner +``` + +Allows the Owner to set the global operator commission in basis points. + +This commission is *used off-chain* when calculating Operator earnings for a given payment distribution. Operator commission is calculated as a percentage of the payment amount made out to each Operator. This commission is deducted from the payment amount, after which the remainder is used to calculate payments made to any Stakers delegated to the Operator. + +*Effects*: +* Sets the `globalOperatorCommissionBips` +* Emits a `GlobalCommissionBipsSet` event + +*Requirements*: +* Caller MUST be the Owner + +#### `setPaymentUpdater` + +```solidity +function setPaymentUpdater(address _paymentUpdater) external onlyOwner +``` + +Allows the Owner to set the `paymentUpdater` address. The `paymentUpdater` is the singleton address that can submit new `DistributionRoots` to the `PaymentCoordinator`. The `paymentUpdater` is a trusted entity that performs the bulk of the calculations and merkle tree structuring described in this document. + +*Effects*: +* Sets the global `paymentUpdater` address +* Emits a `PaymentUpdaterSet` event + +*Requirements*: +* Caller MUST be the Owner + +#### `setPayAllForRangeSubmitter` + +```solidity +function setPayAllForRangeSubmitter(address _submitter, bool _newValue) external onlyOwner +``` + +Allows the Owner to update the `_submitter's` permissions in the `isPayAllForRangeSubmitter` mapping. This mapping is used to determine if a given address is a valid submitter for the `payAllForRange` method. + +*Effects*: +* Sets the `isPayAllForRangeSubmitter` mapping for the address `_submitter` to the bool `_newValue` +* Emits a `PayAllForRangeSubmitterSet` event + +*Requirements*: +* Caller MUST be the Owner + +--- + +### Payments Merkle Tree Structure + +This merkle tree is used to verify the claims against a `DistributionRoot`. + +When submitting a new `DistributionRoot`, the payment updater consolidates all `RangePayments` submitted by AVSs since the previously submitted `DistributionRoot` into a merkle tree comprised of earners and their cumulative earnings for their respective payment tokens distributed. + +When an earner or their designated claimer calls `processClaim`, they must provide a `PaymentMerkleClaim` struct that contains the necessary information to verify their claim against the latest `DistributionRoot`. The merkle proof verification is done in the internal `_checkClaim` helper function. This function verifies the merkle proof of the earner's `EarnerTreeMerkleLeaf` against the `DistributionRoot` and then for each tokenIndex, verifies each token leaf against the earner's `earnerTokenRoot`. + +Claimers can selectively choose which token leaves to prove against and claim accumulated earnings. Each token payment claimed in a `processClaim` call will send tokens to the `recipient` address specified in the call. + +The payment merkle tree is structured in the diagram below: + +![.](../images/PaymentCoordinator_Merkle_Tree.png) + +--- + +### Off Chain Calculation + +Payments are calculated via an off-chain data pipeline. The pipeline takes snapshots of core contract state at the `SNAPSHOT_CADENCE`, currently set to once per day. It then combines these snapshots with any active payments to calculate what the single day payout of an earner is. Every `CALCULATION_INTERVAL_SECONDS` payouts are accumulated up to `lastPaymentTimestamp + CALCULATION_INTERVAL_SECONDS` and posted on-chain by the entity with the `paymentUpdater` role. + +`MAX_PAYMENT_AMOUNT` is set to `1e38-1` given the precision bounds of the off-chain pipeline. \ No newline at end of file diff --git a/docs/images/PaymentCoordinator_Merkle_Tree.png b/docs/images/PaymentCoordinator_Merkle_Tree.png new file mode 100644 index 0000000000000000000000000000000000000000..7727dd8687c78e4e4ae6c6af86a9b4d4e9a385b2 GIT binary patch literal 271396 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

=nyT5?rQPc0|=bVf?vu{L%1 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 literal 0 HcmV?d00001 diff --git a/script/configs/holesky/Deploy_PaymentCoordinator.holesky.config.json b/script/configs/holesky/Deploy_PaymentCoordinator.holesky.config.json new file mode 100644 index 000000000..071b2a7a3 --- /dev/null +++ b/script/configs/holesky/Deploy_PaymentCoordinator.holesky.config.json @@ -0,0 +1,58 @@ +{ + "chainInfo": { + "chainId": 17000 + }, + "multisig_addresses": { + "pauserMultisig": "0x53410249ec7d3a3F9F1ba3912D50D6A3Df6d10A7", + "communityMultisig": "0xCb8d2f9e55Bc7B1FA9d089f9aC80C583D2BDD5F7", + "operationsMultisig": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", + "executorMultisig": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", + "timelock": "0xcF19CE0561052a7A7Ff21156730285997B350A7D" + }, + "strategies": { + "numStrategies": 0, + "MAX_PER_DEPOSIT": 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "MAX_TOTAL_DEPOSITS": 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "strategiesToDeploy": [] + }, + "strategyManager": { + "init_strategy_whitelister": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", + "init_paused_status": 0 + }, + "delegationManager": { + "init_paused_status": 0, + "init_minWithdrawalDelayBlocks": 10 + }, + "paymentCoordinator": { + "init_paused_status": 0, + "CALCULATION_INTERVAL_SECONDS": 604800, + "MAX_PAYMENT_DURATION": 6048000, + "MAX_RETROACTIVE_LENGTH": 7776000, + "MAX_FUTURE_LENGTH": 2592000, + "GENESIS_PAYMENT_TIMESTAMP": 1710979200, + "payment_updater_address": "0x02d9bd32ec711AC8782aEaBF9e1E1309F0965c11", + "activation_delay": 120, + "calculation_interval_seconds": 604800, + "global_operator_commission_bips": 1000 + }, + "avsDirectory": { + "init_paused_status": 0 + }, + "slasher": { + "init_paused_status": 0 + }, + "eigenPod": { + "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": 32000000000, + "GENESIS_TIME": 1695902400 + }, + "eigenPodManager": { + "init_paused_status": 0, + "deneb_fork_timestamp": "1707305664" + }, + "delayedWithdrawalRouter": { + "init_paused_status": 0, + "init_withdrawalDelayBlocks": 10 + }, + "ethPOSDepositAddress": "0x4242424242424242424242424242424242424242", + "beaconOracleAddress": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25" +} \ No newline at end of file diff --git a/script/configs/holesky/Holesky_current_deployment.config.json b/script/configs/holesky/Holesky_current_deployment.config.json index b3cf05dbf..11b4469f1 100644 --- a/script/configs/holesky/Holesky_current_deployment.config.json +++ b/script/configs/holesky/Holesky_current_deployment.config.json @@ -15,8 +15,11 @@ "eigenPodManager": "0x30770d7E3e71112d7A6b7259542D1f680a70e315", "eigenPodManagerImplementation": "0x5265C162f7d5F3fE3175a78828ab16bf5E324a7B", "emptyContract": "0x9690d52B1Ce155DB2ec5eCbF5a262ccCc7B3A6D2", + "paymentCoordinator": "0x0000000000000000000000000000000000000000", + "paymentCoordinatorImplementation": "0x0000000000000000000000000000000000000000", "slasher": "0xcAe751b75833ef09627549868A04E32679386e7C", "slasherImplementation": "0x99715D255E34a39bE9943b82F281CA734bcF345A", + "numStrategiesDeployed": 8, "strategies": { "WETH": "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", "rETH": "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", diff --git a/script/configs/mainnet/Mainnet_current_deployment.config.json b/script/configs/mainnet/Mainnet_current_deployment.config.json index 3bad379bc..50d55ec9d 100644 --- a/script/configs/mainnet/Mainnet_current_deployment.config.json +++ b/script/configs/mainnet/Mainnet_current_deployment.config.json @@ -15,10 +15,13 @@ "eigenPodManager": "0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338", "eigenPodManagerImplementation": "0xEB86a5c40FdE917E6feC440aBbCDc80E3862e111", "emptyContract": "0x1f96861fEFa1065a5A96F20Deb6D8DC3ff48F7f9", + "paymentCoordinator": "0x0000000000000000000000000000000000000000", + "paymentCoordinatorImplementation": "0x0000000000000000000000000000000000000000", "slasher": "0xD92145c07f8Ed1D392c1B88017934E301CC1c3Cd", "slasherImplementation": "0xef31c292801f24f16479DD83197F1E6AeBb8d6d8", "strategyManager": "0x858646372CC42E1A627fcE94aa7A7033e7CF075A", "strategyManagerImplementation": "0x5d25EEf8CfEdaA47d31fE2346726dE1c21e342Fb", + "numStrategiesDeployed": 12, "strategies": { "stETH": "0x93c4b944D05dfe6df7645A86cd2206016c51564D", "rETH": "0x1BeE69b7dFFfA4E2d53C2a2Df135C388AD25dCD2", diff --git a/script/deploy/holesky/Deploy_Preprod_PaymentCoordinator.s.sol b/script/deploy/holesky/Deploy_Preprod_PaymentCoordinator.s.sol new file mode 100644 index 000000000..ef7e60bbe --- /dev/null +++ b/script/deploy/holesky/Deploy_Preprod_PaymentCoordinator.s.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "./Deploy_Test_PaymentCoordinator.s.sol"; + +/** + * @notice Script used for the first deployment of EigenLayer core contracts to Holesky + * anvil --fork-url $RPC_HOLESKY + * Local Fork: Deploy/Upgrade PaymentCoordinator + * forge script script/deploy/holesky/Deploy_Preprod_PaymentCoordinator.s.sol --rpc-url http://127.0.0.1:8545 --private-key $PRIVATE_KEY --broadcast -vvvv --sig "run(string memory deployArg)" deploy + * forge script script/deploy/holesky/Deploy_Preprod_PaymentCoordinator.s.sol --rpc-url http://127.0.0.1:8545 --private-key $PRIVATE_KEY --broadcast -vvvv --sig "run(string memory deployArg)" upgrade + * + * Holesky testnet: Deploy/Upgrade PaymentCoordinator + * forge script script/deploy/holesky/Deploy_Preprod_PaymentCoordinator.s.sol --rpc-url $RPC_HOLESKY --private-key $PRIVATE_KEY --broadcast -vvvv --sig "run(string memory deployArg)" deploy + * forge script script/deploy/holesky/Deploy_Preprod_PaymentCoordinator.s.sol --rpc-url $RPC_HOLESKY --private-key $PRIVATE_KEY --broadcast -vvvv --sig "run(string memory deployArg)" upgrade + * + */ +contract Deploy_Preprod_PaymentCoordinator is Deploy_Test_PaymentCoordinator { + function run(string memory deployArg) external virtual { + _parseInitialDeploymentParams("script/configs/holesky/Deploy_PaymentCoordinator.holesky.config.json"); + _parseDeployedContracts("script/output/holesky/M2_deploy_preprod.output.json"); + + // Overwrite testAddress and multisigs to be EOAowner + testAddress = msg.sender; + executorMultisig = testAddress; + operationsMultisig = testAddress; + pauserMultisig = testAddress; + communityMultisig = testAddress; + STRATEGY_MANAGER_WHITELISTER = testAddress; + + // START RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.startBroadcast(); + + emit log_named_address("Deployer Address", msg.sender); + + if (keccak256(abi.encode(deployArg)) == keccak256(abi.encode("upgrade"))) { + _upgradePaymentCoordinator(); + } else if (keccak256(abi.encode(deployArg)) == keccak256(abi.encode("deploy"))) { + _deployPaymentCoordinator(); + } + + // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.stopBroadcast(); + + // Sanity Checks + _verifyContractPointers(); + _verifyImplementations(); + _verifyContractsInitialized({isInitialDeployment: true}); + _verifyInitializationParams(); + + logAndOutputContractAddresses("script/output/holesky/Deploy_PaymentCoordinator_Preprod.holesky.config.json"); + } +} diff --git a/script/deploy/holesky/Deploy_Test_PaymentCoordinator.s.sol b/script/deploy/holesky/Deploy_Test_PaymentCoordinator.s.sol new file mode 100644 index 000000000..59dd62dd1 --- /dev/null +++ b/script/deploy/holesky/Deploy_Test_PaymentCoordinator.s.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "../../utils/ExistingDeploymentParser.sol"; + +/** + * @notice Script used for the first deployment of EigenLayer core contracts to Holesky + * anvil --fork-url $RPC_HOLESKY + * forge script script/deploy/holesky/Deploy_Test_PaymentCoordinator.s.sol --rpc-url http://127.0.0.1:8545 --private-key $PRIVATE_KEY --broadcast -vvvv + * forge script script/deploy/holesky/Deploy_Test_PaymentCoordinator.s.sol --rpc-url $RPC_HOLESKY --private-key $PRIVATE_KEY --broadcast -vvvv + * + */ +contract Deploy_Test_PaymentCoordinator is ExistingDeploymentParser { + + address testAddress = 0xDA29BB71669f46F2a779b4b62f03644A84eE3479; + + function run() external virtual { + _parseInitialDeploymentParams("script/configs/holesky/Deploy_PaymentCoordinator.holesky.config.json"); + _parseDeployedContracts("script/output/holesky/M2_deploy_from_scratch.output.json"); + + // START RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.startBroadcast(); + + emit log_named_address("Deployer Address", msg.sender); + + _deployPaymentCoordinator(); + + // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.stopBroadcast(); + + // Sanity Checks + _verifyContractPointers(); + _verifyImplementations(); + _verifyContractsInitialized({isInitialDeployment: true}); + _verifyInitializationParams(); + + logAndOutputContractAddresses("script/output/holesky/Deploy_PaymentCoordinator.holesky.config.json"); + } + + /** + * @notice Deploy PaymentCoordinator for Holesky + */ + function _deployPaymentCoordinator() internal { + // Deploy PaymentCoordinator proxy and implementation + paymentCoordinatorImplementation = new PaymentCoordinator( + delegationManager, + strategyManager, + PAYMENT_COORDINATOR_CALCULATION_INTERVAL_SECONDS, + PAYMENT_COORDINATOR_MAX_PAYMENT_DURATION, + PAYMENT_COORDINATOR_MAX_RETROACTIVE_LENGTH, + PAYMENT_COORDINATOR_MAX_FUTURE_LENGTH, + PAYMENT_COORDINATOR_GENESIS_PAYMENT_TIMESTAMP + ); + paymentCoordinator = PaymentCoordinator( + address( + new TransparentUpgradeableProxy( + address(paymentCoordinatorImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector( + PaymentCoordinator.initialize.selector, + testAddress, // initOwner + eigenLayerPauserReg, + PAYMENT_COORDINATOR_INIT_PAUSED_STATUS, + PAYMENT_COORDINATOR_UPDATER, + PAYMENT_COORDINATOR_ACTIVATION_DELAY, + PAYMENT_COORDINATOR_GLOBAL_OPERATOR_COMMISSION_BIPS + ) + ) + ) + ); + + + } + + /** + * @notice Deploy PaymentCoordinator Implementation for Holesky and upgrade the proxy + */ + function _upgradePaymentCoordinator() internal { + // Deploy PaymentCoordinator proxy and implementation + paymentCoordinatorImplementation = new PaymentCoordinator( + delegationManager, + strategyManager, + PAYMENT_COORDINATOR_CALCULATION_INTERVAL_SECONDS, + PAYMENT_COORDINATOR_MAX_PAYMENT_DURATION, + PAYMENT_COORDINATOR_MAX_RETROACTIVE_LENGTH, + PAYMENT_COORDINATOR_MAX_FUTURE_LENGTH, + PAYMENT_COORDINATOR_GENESIS_PAYMENT_TIMESTAMP + ); + + eigenLayerProxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(paymentCoordinator))), + address(paymentCoordinatorImplementation) + ); + } +} diff --git a/script/output/holesky/Deploy_PaymentCoordinator.holesky.config.json b/script/output/holesky/Deploy_PaymentCoordinator.holesky.config.json new file mode 100644 index 000000000..d57a18ad8 --- /dev/null +++ b/script/output/holesky/Deploy_PaymentCoordinator.holesky.config.json @@ -0,0 +1,37 @@ +{ + "addresses": { + "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", + "avsDirectoryImplementation": "0xEF5BA995Bc7722fd1e163edF8Dc09375de3d3e3a", + "baseStrategyImplementation": "0xFb83e1D133D0157775eC4F19Ff81478Df1103305", + "beaconOracle": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25", + "delayedWithdrawalRouter": "0x642c646053eaf2254f088e9019ACD73d9AE0FA32", + "delayedWithdrawalRouterImplementation": "0xcE8b8D99773a718423F8040a6e52c06a4ce63407", + "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", + "delegationManagerImplementation": "0x83f8F8f0BB125F7870F6bfCf76853f874C330D76", + "eigenLayerPauserReg": "0x85Ef7299F8311B25642679edBF02B62FA2212F06", + "eigenLayerProxyAdmin": "0xDB023566064246399b4AE851197a97729C93A6cf", + "eigenPodBeacon": "0x7261C2bd75a7ACE1762f6d7FAe8F63215581832D", + "eigenPodImplementation": "0xe98f9298344527608A1BCC23907B8145F9Cb641c", + "eigenPodManager": "0x30770d7E3e71112d7A6b7259542D1f680a70e315", + "eigenPodManagerImplementation": "0x5265C162f7d5F3fE3175a78828ab16bf5E324a7B", + "emptyContract": "0x9690d52B1Ce155DB2ec5eCbF5a262ccCc7B3A6D2", + "paymentCoordinator": "0x5fC97864488C2B6fA6E5d2D7730090d7908Db98D", + "paymentCoordinatorImplementation": "0x4B034d112b8d422665B13F791A4e1104894e0463", + "slasher": "0xcAe751b75833ef09627549868A04E32679386e7C", + "slasherImplementation": "0x99715D255E34a39bE9943b82F281CA734bcF345A", + "strategies": "", + "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", + "strategyManagerImplementation": "0x59f766A603C53f3AC8Be43bBe158c1519b193a18" + }, + "chainInfo": { + "chainId": 17000, + "deploymentBlock": 1471383 + }, + "parameters": { + "communityMultisig": "0xCb8d2f9e55Bc7B1FA9d089f9aC80C583D2BDD5F7", + "executorMultisig": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", + "operationsMultisig": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", + "pauserMultisig": "0x53410249ec7d3a3F9F1ba3912D50D6A3Df6d10A7", + "timelock": "0xcF19CE0561052a7A7Ff21156730285997B350A7D" + } +} \ No newline at end of file diff --git a/script/output/holesky/Deploy_PaymentCoordinator_Preprod.holesky.config.json b/script/output/holesky/Deploy_PaymentCoordinator_Preprod.holesky.config.json new file mode 100644 index 000000000..37c7607ff --- /dev/null +++ b/script/output/holesky/Deploy_PaymentCoordinator_Preprod.holesky.config.json @@ -0,0 +1,37 @@ +{ + "addresses": { + "avsDirectory": "0x141d6995556135D4997b2ff72EB443Be300353bC", + "avsDirectoryImplementation": "0x357978adC03375BD6a3605DE055fABb84695d79A", + "baseStrategyImplementation": "0x62450517EfA1CE60d79801daf8f95973865e8D40", + "beaconOracle": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25", + "delayedWithdrawalRouter": "0xC4BC46a87A67a531eCF7f74338E1FA79533334Fa", + "delayedWithdrawalRouterImplementation": "0x0011FA2c512063C495f77296Af8d195F33A8Dd38", + "delegationManager": "0x75dfE5B44C2E530568001400D3f704bC8AE350CC", + "delegationManagerImplementation": "0x56E88cb4f0136fC27D95499dE4BE2acf47946Fa1", + "eigenLayerPauserReg": "0x9Ab2FEAf0465f0eD51Fc2b663eF228B418c9Dad1", + "eigenLayerProxyAdmin": "0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B", + "eigenPodBeacon": "0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC", + "eigenPodImplementation": "0x2D6c7f9862BD80Cf0d9d93FC6b513D69E7Db7869", + "eigenPodManager": "0xB8d8952f572e67B11e43bC21250967772fa883Ff", + "eigenPodManagerImplementation": "0xc5B857A92245f64e9D90cCc5b096Db82eB77eB5c", + "emptyContract": "0x9690d52B1Ce155DB2ec5eCbF5a262ccCc7B3A6D2", + "paymentCoordinator": "0xb22Ef643e1E067c994019A4C19e403253C05c2B0", + "paymentCoordinatorImplementation": "0xC9366ab4A299e0937EC15A6C256C4481C05A24fD", + "slasher": "0x12699471dF8dca329C76D72823B1b79d55709384", + "slasherImplementation": "0x9460fCe11E1e0365419fa860599903B4E5097cf0", + "strategies": "", + "strategyManager": "0xF9fbF2e35D8803273E214c99BF15174139f4E67a", + "strategyManagerImplementation": "0x1a26B23a004C512350d7Dd89056655A80b850199" + }, + "chainInfo": { + "chainId": 17000, + "deploymentBlock": 1477016 + }, + "parameters": { + "communityMultisig": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479", + "executorMultisig": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479", + "operationsMultisig": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479", + "pauserMultisig": "0xDA29BB71669f46F2a779b4b62f03644A84eE3479", + "timelock": "0xcF19CE0561052a7A7Ff21156730285997B350A7D" + } +} \ No newline at end of file diff --git a/script/output/holesky/M2_deploy_from_scratch.output.json b/script/output/holesky/M2_deploy_from_scratch.output.json new file mode 100644 index 000000000..4a401becd --- /dev/null +++ b/script/output/holesky/M2_deploy_from_scratch.output.json @@ -0,0 +1,61 @@ +{ + "addresses": { + "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", + "avsDirectoryImplementation": "0xEF5BA995Bc7722fd1e163edF8Dc09375de3d3e3a", + "baseStrategyImplementation": "0xFb83e1D133D0157775eC4F19Ff81478Df1103305", + "beaconOracle": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25", + "delayedWithdrawalRouter": "0x642c646053eaf2254f088e9019ACD73d9AE0FA32", + "delayedWithdrawalRouterImplementation": "0xcE8b8D99773a718423F8040a6e52c06a4ce63407", + "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", + "delegationManagerImplementation": "0x83f8F8f0BB125F7870F6bfCf76853f874C330D76", + "eigenLayerPauserReg": "0x85Ef7299F8311B25642679edBF02B62FA2212F06", + "eigenLayerProxyAdmin": "0xDB023566064246399b4AE851197a97729C93A6cf", + "eigenPodBeacon": "0x7261C2bd75a7ACE1762f6d7FAe8F63215581832D", + "eigenPodImplementation": "0xe98f9298344527608A1BCC23907B8145F9Cb641c", + "eigenPodManager": "0x30770d7E3e71112d7A6b7259542D1f680a70e315", + "eigenPodManagerImplementation": "0x5265C162f7d5F3fE3175a78828ab16bf5E324a7B", + "emptyContract": "0x9690d52B1Ce155DB2ec5eCbF5a262ccCc7B3A6D2", + "slasher": "0xcAe751b75833ef09627549868A04E32679386e7C", + "slasherImplementation": "0x99715D255E34a39bE9943b82F281CA734bcF345A", + "numStrategiesDeployed": 10, + "strategies": { + "WETH": "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", + "rETH": "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", + "stETH": "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", + "ETHx": "0x31B6F59e1627cEfC9fA174aD03859fC337666af7", + "cbETH": "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6", + "sfrxETH": "0x9281ff96637710Cd9A5CAcce9c6FAD8C9F54631c", + "lsETH": "0x05037A81BD7B4C9E0F7B430f1F2A22c31a2FD943", + "osETH": "0x46281E3B7fDcACdBa44CADf069a94a588Fd4C6Ef", + "mETH": "0xaccc5A86732BE85b5012e8614AF237801636F8e5", + "ankrETH" :"0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac" + }, + "strategyAddresses": [ + "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", + "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", + "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", + "0x31B6F59e1627cEfC9fA174aD03859fC337666af7", + "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6", + "0x9281ff96637710Cd9A5CAcce9c6FAD8C9F54631c", + "0x05037A81BD7B4C9E0F7B430f1F2A22c31a2FD943", + "0x46281E3B7fDcACdBa44CADf069a94a588Fd4C6Ef", + "0xaccc5A86732BE85b5012e8614AF237801636F8e5", + "0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac" + ], + "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", + "strategyManagerImplementation": "0x59f766A603C53f3AC8Be43bBe158c1519b193a18", + "paymentCoordinator": "0x0000000000000000000000000000000000000000", + "paymentCoordinatorImplementation": "0x0000000000000000000000000000000000000000" + }, + "chainInfo": { + "chainId": 17000, + "deploymentBlock": 1195642 + }, + "parameters": { + "communityMultisig": "0xCb8d2f9e55Bc7B1FA9d089f9aC80C583D2BDD5F7", + "executorMultisig": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", + "operationsMultisig": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", + "pauserMultisig": "0x53410249ec7d3a3F9F1ba3912D50D6A3Df6d10A7", + "timelock": "0xcF19CE0561052a7A7Ff21156730285997B350A7D" + } +} diff --git a/script/output/holesky/M2_deploy_preprod.output.json b/script/output/holesky/M2_deploy_preprod.output.json new file mode 100644 index 000000000..97689da15 --- /dev/null +++ b/script/output/holesky/M2_deploy_preprod.output.json @@ -0,0 +1,39 @@ +{ + "addresses": { + "avsDirectory": "0x141d6995556135D4997b2ff72EB443Be300353bC", + "avsDirectoryImplementation": "0x357978adC03375BD6a3605DE055fABb84695d79A", + "baseStrategyImplementation": "0x62450517EfA1CE60d79801daf8f95973865e8D40", + "beaconOracle": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25", + "delayedWithdrawalRouter": "0xC4BC46a87A67a531eCF7f74338E1FA79533334Fa", + "delayedWithdrawalRouterImplementation": "0x0011FA2c512063C495f77296Af8d195F33A8Dd38", + "delegationManager": "0x75dfE5B44C2E530568001400D3f704bC8AE350CC", + "delegationManagerImplementation": "0x56E88cb4f0136fC27D95499dE4BE2acf47946Fa1", + "eigenLayerPauserReg": "0x9Ab2FEAf0465f0eD51Fc2b663eF228B418c9Dad1", + "eigenLayerProxyAdmin": "0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B", + "eigenPodBeacon": "0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC", + "eigenPodImplementation": "0x2D6c7f9862BD80Cf0d9d93FC6b513D69E7Db7869", + "eigenPodManager": "0xB8d8952f572e67B11e43bC21250967772fa883Ff", + "eigenPodManagerImplementation": "0xc5B857A92245f64e9D90cCc5b096Db82eB77eB5c", + "emptyContract": "0x9690d52B1Ce155DB2ec5eCbF5a262ccCc7B3A6D2", + "slasher": "0x12699471dF8dca329C76D72823B1b79d55709384", + "slasherImplementation": "0x9460fCe11E1e0365419fa860599903B4E5097cf0", + "numStrategiesDeployed": 0, + "strategies": {}, + "strategyAddresses": [], + "strategyManager": "0xF9fbF2e35D8803273E214c99BF15174139f4E67a", + "strategyManagerImplementation": "0x1a26B23a004C512350d7Dd89056655A80b850199", + "paymentCoordinator": "0xb22Ef643e1E067c994019A4C19e403253C05c2B0", + "paymentCoordinatorImplementation": "0xC9366ab4A299e0937EC15A6C256C4481C05A24fD" + }, + "chainInfo": { + "chainId": 17000, + "deploymentBlock": 1195642 + }, + "parameters": { + "communityMultisig": "0xCb8d2f9e55Bc7B1FA9d089f9aC80C583D2BDD5F7", + "executorMultisig": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", + "operationsMultisig": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", + "pauserMultisig": "0x53410249ec7d3a3F9F1ba3912D50D6A3Df6d10A7", + "timelock": "0xcF19CE0561052a7A7Ff21156730285997B350A7D" + } +} diff --git a/script/utils/ExistingDeploymentParser.sol b/script/utils/ExistingDeploymentParser.sol index 352b6a9db..14447992e 100644 --- a/script/utils/ExistingDeploymentParser.sol +++ b/script/utils/ExistingDeploymentParser.sol @@ -9,6 +9,7 @@ import "../../src/contracts/core/StrategyManager.sol"; import "../../src/contracts/core/Slasher.sol"; import "../../src/contracts/core/DelegationManager.sol"; import "../../src/contracts/core/AVSDirectory.sol"; +import "../../src/contracts/core/PaymentCoordinator.sol"; import "../../src/contracts/strategies/StrategyBase.sol"; import "../../src/contracts/strategies/StrategyBaseTVLLimits.sol"; @@ -52,6 +53,8 @@ contract ExistingDeploymentParser is Script, Test { DelegationManager public delegationManagerImplementation; StrategyManager public strategyManager; StrategyManager public strategyManagerImplementation; + PaymentCoordinator public paymentCoordinator; + PaymentCoordinator public paymentCoordinatorImplementation; EigenPodManager public eigenPodManager; EigenPodManager public eigenPodManagerImplementation; DelayedWithdrawalRouter public delayedWithdrawalRouter; @@ -103,6 +106,16 @@ contract ExistingDeploymentParser is Script, Test { uint256 DELEGATION_MANAGER_MIN_WITHDRAWAL_DELAY_BLOCKS; // AVSDirectory uint256 AVS_DIRECTORY_INIT_PAUSED_STATUS; + // PaymentCoordinator + uint256 PAYMENT_COORDINATOR_INIT_PAUSED_STATUS; + uint32 PAYMENT_COORDINATOR_MAX_PAYMENT_DURATION; + uint32 PAYMENT_COORDINATOR_MAX_RETROACTIVE_LENGTH; + uint32 PAYMENT_COORDINATOR_MAX_FUTURE_LENGTH; + uint32 PAYMENT_COORDINATOR_GENESIS_PAYMENT_TIMESTAMP; + address PAYMENT_COORDINATOR_UPDATER; + uint32 PAYMENT_COORDINATOR_ACTIVATION_DELAY; + uint32 PAYMENT_COORDINATOR_CALCULATION_INTERVAL_SECONDS; + uint32 PAYMENT_COORDINATOR_GLOBAL_OPERATOR_COMMISSION_BIPS; // EigenPodManager uint256 EIGENPOD_MANAGER_INIT_PAUSED_STATUS; uint64 EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP; @@ -149,7 +162,9 @@ contract ExistingDeploymentParser is Script, Test { slasherImplementation = Slasher( stdJson.readAddress(existingDeploymentData, ".addresses.slasherImplementation") ); - delegationManager = DelegationManager(stdJson.readAddress(existingDeploymentData, ".addresses.delegationManager")); + delegationManager = DelegationManager( + stdJson.readAddress(existingDeploymentData, ".addresses.delegationManager") + ); delegationManagerImplementation = DelegationManager( stdJson.readAddress(existingDeploymentData, ".addresses.delegationManagerImplementation") ); @@ -157,6 +172,12 @@ contract ExistingDeploymentParser is Script, Test { avsDirectoryImplementation = AVSDirectory( stdJson.readAddress(existingDeploymentData, ".addresses.avsDirectoryImplementation") ); + paymentCoordinator = PaymentCoordinator( + stdJson.readAddress(existingDeploymentData, ".addresses.paymentCoordinator") + ); + paymentCoordinatorImplementation = PaymentCoordinator( + stdJson.readAddress(existingDeploymentData, ".addresses.paymentCoordinatorImplementation") + ); strategyManager = StrategyManager(stdJson.readAddress(existingDeploymentData, ".addresses.strategyManager")); strategyManagerImplementation = StrategyManager( stdJson.readAddress(existingDeploymentData, ".addresses.strategyManagerImplementation") @@ -171,9 +192,7 @@ contract ExistingDeploymentParser is Script, Test { delayedWithdrawalRouterImplementation = DelayedWithdrawalRouter( stdJson.readAddress(existingDeploymentData, ".addresses.delayedWithdrawalRouterImplementation") ); - beaconOracle = IBeaconChainOracle( - stdJson.readAddress(existingDeploymentData, ".addresses.beaconOracle") - ); + beaconOracle = IBeaconChainOracle(stdJson.readAddress(existingDeploymentData, ".addresses.beaconOracle")); eigenPodBeacon = UpgradeableBeacon(stdJson.readAddress(existingDeploymentData, ".addresses.eigenPodBeacon")); eigenPodImplementation = EigenPod( payable(stdJson.readAddress(existingDeploymentData, ".addresses.eigenPodImplementation")) @@ -184,7 +203,7 @@ contract ExistingDeploymentParser is Script, Test { emptyContract = EmptyContract(stdJson.readAddress(existingDeploymentData, ".addresses.emptyContract")); // Strategies Deployed, load strategy list - numStrategiesDeployed = stdJson.readUint(existingDeploymentData, ".numStrategies"); + numStrategiesDeployed = stdJson.readUint(existingDeploymentData, ".addresses.numStrategiesDeployed"); for (uint256 i = 0; i < numStrategiesDeployed; ++i) { // Form the key for the current element string memory key = string.concat(".addresses.strategyAddresses[", vm.toString(i), "]"); @@ -273,7 +292,10 @@ contract ExistingDeploymentParser is Script, Test { initialDeploymentData, ".strategyManager.init_paused_status" ); - STRATEGY_MANAGER_WHITELISTER = stdJson.readAddress(initialDeploymentData, ".strategyManager.init_strategy_whitelister"); + STRATEGY_MANAGER_WHITELISTER = stdJson.readAddress( + initialDeploymentData, + ".strategyManager.init_strategy_whitelister" + ); // Slasher SLASHER_INIT_PAUSED_STATUS = stdJson.readUint(initialDeploymentData, ".slasher.init_paused_status"); // DelegationManager @@ -285,6 +307,26 @@ contract ExistingDeploymentParser is Script, Test { initialDeploymentData, ".delegationManager.init_paused_status" ); + // PaymentCoordinator + PAYMENT_COORDINATOR_INIT_PAUSED_STATUS = stdJson.readUint( + initialDeploymentData, + ".paymentCoordinator.init_paused_status" + ); + PAYMENT_COORDINATOR_CALCULATION_INTERVAL_SECONDS = uint32( + stdJson.readUint(initialDeploymentData, ".paymentCoordinator.CALCULATION_INTERVAL_SECONDS") + ); + PAYMENT_COORDINATOR_MAX_PAYMENT_DURATION = uint32(stdJson.readUint(initialDeploymentData, ".paymentCoordinator.MAX_PAYMENT_DURATION")); + PAYMENT_COORDINATOR_MAX_RETROACTIVE_LENGTH = uint32(stdJson.readUint(initialDeploymentData, ".paymentCoordinator.MAX_RETROACTIVE_LENGTH")); + PAYMENT_COORDINATOR_MAX_FUTURE_LENGTH = uint32(stdJson.readUint(initialDeploymentData, ".paymentCoordinator.MAX_FUTURE_LENGTH")); + PAYMENT_COORDINATOR_GENESIS_PAYMENT_TIMESTAMP = uint32(stdJson.readUint(initialDeploymentData, ".paymentCoordinator.GENESIS_PAYMENT_TIMESTAMP")); + PAYMENT_COORDINATOR_UPDATER = stdJson.readAddress(initialDeploymentData, ".paymentCoordinator.payment_updater_address"); + PAYMENT_COORDINATOR_ACTIVATION_DELAY = uint32(stdJson.readUint(initialDeploymentData, ".paymentCoordinator.activation_delay")); + PAYMENT_COORDINATOR_CALCULATION_INTERVAL_SECONDS = uint32( + stdJson.readUint(initialDeploymentData, ".paymentCoordinator.calculation_interval_seconds") + ); + PAYMENT_COORDINATOR_GLOBAL_OPERATOR_COMMISSION_BIPS = uint32( + stdJson.readUint(initialDeploymentData, ".paymentCoordinator.global_operator_commission_bips") + ); // AVSDirectory AVS_DIRECTORY_INIT_PAUSED_STATUS = stdJson.readUint(initialDeploymentData, ".avsDirectory.init_paused_status"); // EigenPodManager @@ -292,10 +334,9 @@ contract ExistingDeploymentParser is Script, Test { initialDeploymentData, ".eigenPodManager.init_paused_status" ); - EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP = uint64(stdJson.readUint( - initialDeploymentData, - ".eigenPodManager.deneb_fork_timestamp" - )); + EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP = uint64( + stdJson.readUint(initialDeploymentData, ".eigenPodManager.deneb_fork_timestamp") + ); // EigenPod EIGENPOD_GENESIS_TIME = uint64(stdJson.readUint(initialDeploymentData, ".eigenPod.GENESIS_TIME")); @@ -318,12 +359,21 @@ contract ExistingDeploymentParser is Script, Test { } /// @notice Ensure contracts point at each other correctly via constructors - function _verifyContractPointers() internal virtual view { + function _verifyContractPointers() internal view virtual { // AVSDirectory require( avsDirectory.delegation() == delegationManager, "avsDirectory: delegationManager address not set correctly" ); + // PaymentCoordinator + require( + paymentCoordinator.delegationManager() == delegationManager, + "paymentCoordinator: delegationManager address not set correctly" + ); + require( + paymentCoordinator.strategyManager() == strategyManager, + "paymentCoordinator: strategyManager address not set correctly" + ); // DelegationManager require(delegationManager.slasher() == slasher, "delegationManager: slasher address not set correctly"); require( @@ -370,12 +420,18 @@ contract ExistingDeploymentParser is Script, Test { } /// @notice verify implementations for Transparent Upgradeable Proxies - function _verifyImplementations() internal virtual view { + function _verifyImplementations() internal view virtual { require( eigenLayerProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(avsDirectory)))) == address(avsDirectoryImplementation), "avsDirectory: implementation set incorrectly" ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(paymentCoordinator))) + ) == address(paymentCoordinatorImplementation), + "paymentCoordinator: implementation set incorrectly" + ); require( eigenLayerProxyAdmin.getProxyImplementation( TransparentUpgradeableProxy(payable(address(delegationManager))) @@ -426,10 +482,20 @@ contract ExistingDeploymentParser is Script, Test { * initialization params if this is the first deployment. * @param isInitialDeployment True if this is the first deployment of contracts from scratch */ - function _verifyContractsInitialized(bool isInitialDeployment) internal virtual{ + function _verifyContractsInitialized(bool isInitialDeployment) internal virtual { // AVSDirectory vm.expectRevert(bytes("Initializable: contract is already initialized")); avsDirectory.initialize(address(0), eigenLayerPauserReg, AVS_DIRECTORY_INIT_PAUSED_STATUS); + // PaymentCoordinator + vm.expectRevert(bytes("Initializable: contract is already initialized")); + paymentCoordinator.initialize( + address(0), + eigenLayerPauserReg, + 0, // initialPausedStatus + address(0), // paymentUpdater + 0, // activationDelay + 0 // globalCommissionBips + ); // DelegationManager vm.expectRevert(bytes("Initializable: contract is already initialized")); IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); @@ -447,12 +513,7 @@ contract ExistingDeploymentParser is Script, Test { strategyManager.initialize(address(0), address(0), eigenLayerPauserReg, STRATEGY_MANAGER_INIT_PAUSED_STATUS); // EigenPodManager vm.expectRevert(bytes("Initializable: contract is already initialized")); - eigenPodManager.initialize( - beaconOracle, - address(0), - eigenLayerPauserReg, - EIGENPOD_MANAGER_INIT_PAUSED_STATUS - ); + eigenPodManager.initialize(beaconOracle, address(0), eigenLayerPauserReg, EIGENPOD_MANAGER_INIT_PAUSED_STATUS); // DelayedWithdrawalRouter vm.expectRevert(bytes("Initializable: contract is already initialized")); delayedWithdrawalRouter.initialize( @@ -474,7 +535,7 @@ contract ExistingDeploymentParser is Script, Test { } /// @notice Verify params based on config constants that are updated from calling `_parseInitialDeploymentParams` - function _verifyInitializationParams() internal virtual view { + function _verifyInitializationParams() internal view virtual { // AVSDirectory require( avsDirectory.pauserRegistry() == eigenLayerPauserReg, @@ -485,6 +546,51 @@ contract ExistingDeploymentParser is Script, Test { avsDirectory.paused() == AVS_DIRECTORY_INIT_PAUSED_STATUS, "avsdirectory: init paused status set incorrectly" ); + // PaymentCoordinator + require( + paymentCoordinator.pauserRegistry() == eigenLayerPauserReg, + "paymentCoordinator: pauser registry not set correctly" + ); + // require( + // paymentCoordinator.owner() == executorMultisig, + // "paymentCoordinator: owner not set correctly" + // ); + require( + paymentCoordinator.paused() == PAYMENT_COORDINATOR_INIT_PAUSED_STATUS, + "paymentCoordinator: init paused status set incorrectly" + ); + require( + paymentCoordinator.MAX_PAYMENT_DURATION() == PAYMENT_COORDINATOR_MAX_PAYMENT_DURATION, + "paymentCoordinator: maxPaymentDuration not set correctly" + ); + require( + paymentCoordinator.MAX_RETROACTIVE_LENGTH() == PAYMENT_COORDINATOR_MAX_RETROACTIVE_LENGTH, + "paymentCoordinator: maxRetroactiveLength not set correctly" + ); + require( + paymentCoordinator.MAX_FUTURE_LENGTH() == PAYMENT_COORDINATOR_MAX_FUTURE_LENGTH, + "paymentCoordinator: maxFutureLength not set correctly" + ); + require( + paymentCoordinator.GENESIS_PAYMENT_TIMESTAMP() == PAYMENT_COORDINATOR_GENESIS_PAYMENT_TIMESTAMP, + "paymentCoordinator: genesisPaymentTimestamp not set correctly" + ); + require( + paymentCoordinator.paymentUpdater() == PAYMENT_COORDINATOR_UPDATER, + "paymentCoordinator: paymentUpdater not set correctly" + ); + require( + paymentCoordinator.activationDelay() == PAYMENT_COORDINATOR_ACTIVATION_DELAY, + "paymentCoordinator: activationDelay not set correctly" + ); + require( + paymentCoordinator.CALCULATION_INTERVAL_SECONDS() == PAYMENT_COORDINATOR_CALCULATION_INTERVAL_SECONDS, + "paymentCoordinator: CALCULATION_INTERVAL_SECONDS not set correctly" + ); + require( + paymentCoordinator.globalOperatorCommissionBips() == PAYMENT_COORDINATOR_GLOBAL_OPERATOR_COMMISSION_BIPS, + "paymentCoordinator: globalCommissionBips not set correctly" + ); // DelegationManager require( delegationManager.pauserRegistry() == eigenLayerPauserReg, @@ -509,10 +615,18 @@ contract ExistingDeploymentParser is Script, Test { strategyManager.paused() == STRATEGY_MANAGER_INIT_PAUSED_STATUS, "strategyManager: init paused status set incorrectly" ); - require( - strategyManager.strategyWhitelister() == operationsMultisig, - "strategyManager: strategyWhitelister not set correctly" - ); + if (block.chainid == 1) { + require( + strategyManager.strategyWhitelister() == operationsMultisig, + "strategyManager: strategyWhitelister not set correctly" + ); + } else if (block.chainid == 17000) { + // On holesky, for ease of whitelisting we set to executorMultisig + require( + strategyManager.strategyWhitelister() == executorMultisig, + "strategyManager: strategyWhitelister not set correctly" + ); + } // EigenPodManager require( eigenPodManager.pauserRegistry() == eigenLayerPauserReg, @@ -544,8 +658,8 @@ contract ExistingDeploymentParser is Script, Test { ); require( eigenPodImplementation.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() == - EIGENPOD_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR - && EIGENPOD_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR % 1 gwei == 0, + EIGENPOD_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR && + EIGENPOD_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR % 1 gwei == 0, "eigenPodImplementation: MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR not set correctly" ); require( @@ -609,6 +723,8 @@ contract ExistingDeploymentParser is Script, Test { ); emit log_named_uint("DELEGATION_MANAGER_INIT_PAUSED_STATUS", DELEGATION_MANAGER_INIT_PAUSED_STATUS); emit log_named_uint("AVS_DIRECTORY_INIT_PAUSED_STATUS", AVS_DIRECTORY_INIT_PAUSED_STATUS); + emit log_named_uint("PAYMENT_COORDINATOR_INIT_PAUSED_STATUS", PAYMENT_COORDINATOR_INIT_PAUSED_STATUS); + // todo log all payment coordinator params emit log_named_uint("EIGENPOD_MANAGER_INIT_PAUSED_STATUS", EIGENPOD_MANAGER_INIT_PAUSED_STATUS); emit log_named_uint("EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP", EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP); emit log_named_uint("EIGENPOD_GENESIS_TIME", EIGENPOD_GENESIS_TIME); @@ -647,7 +763,11 @@ contract ExistingDeploymentParser is Script, Test { string memory deployed_strategies = "strategies"; for (uint256 i = 0; i < numStrategiesToDeploy; ++i) { - vm.serializeAddress(deployed_strategies, strategiesToDeploy[i].tokenSymbol, address(deployedStrategyArray[i])); + vm.serializeAddress( + deployed_strategies, + strategiesToDeploy[i].tokenSymbol, + address(deployedStrategyArray[i]) + ); } string memory deployed_strategies_output = numStrategiesToDeploy == 0 ? "" @@ -665,13 +785,23 @@ contract ExistingDeploymentParser is Script, Test { vm.serializeAddress(deployed_addresses, "avsDirectory", address(avsDirectory)); vm.serializeAddress(deployed_addresses, "avsDirectoryImplementation", address(avsDirectoryImplementation)); vm.serializeAddress(deployed_addresses, "delegationManager", address(delegationManager)); - vm.serializeAddress(deployed_addresses, "delegationManagerImplementation", address(delegationManagerImplementation)); + vm.serializeAddress( + deployed_addresses, + "delegationManagerImplementation", + address(delegationManagerImplementation) + ); vm.serializeAddress(deployed_addresses, "strategyManager", address(strategyManager)); vm.serializeAddress( deployed_addresses, "strategyManagerImplementation", address(strategyManagerImplementation) ); + vm.serializeAddress(deployed_addresses, "paymentCoordinator", address(paymentCoordinator)); + vm.serializeAddress( + deployed_addresses, + "paymentCoordinatorImplementation", + address(paymentCoordinatorImplementation) + ); vm.serializeAddress(deployed_addresses, "eigenPodManager", address(eigenPodManager)); vm.serializeAddress( deployed_addresses, @@ -713,6 +843,5 @@ contract ExistingDeploymentParser is Script, Test { string memory finalJson = vm.serializeString(parent_object, parameters, parameters_output); vm.writeJson(finalJson, outputPath); - } } diff --git a/src/contracts/core/PaymentCoordinator.sol b/src/contracts/core/PaymentCoordinator.sol new file mode 100644 index 000000000..1fff906bc --- /dev/null +++ b/src/contracts/core/PaymentCoordinator.sol @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; +import "@openzeppelin-upgrades/contracts/security/ReentrancyGuardUpgradeable.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "../libraries/Merkle.sol"; +import "../libraries/EIP1271SignatureUtils.sol"; +import "../permissions/Pausable.sol"; +import "./PaymentCoordinatorStorage.sol"; + +/** + * @title PaymentCoordinator + * @author Eigen Labs Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice This is the contract for payments in EigenLayer. The main functionalities of this contract are + * - enabling any ERC20 payments from AVSs to their operators and stakers for a given time range + * - allowing stakers and operators to claim their earnings including a commission bips for operators + * - allowing the protocol to provide ERC20 tokens to stakers over a specified time range + */ +contract PaymentCoordinator is + Initializable, + OwnableUpgradeable, + Pausable, + ReentrancyGuardUpgradeable, + PaymentCoordinatorStorage +{ + using SafeERC20 for IERC20; + + /// @notice The EIP-712 typehash for the contract's domain + bytes32 internal constant DOMAIN_TYPEHASH = + keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); + /// @dev Chain ID at the time of contract deployment + uint256 internal immutable ORIGINAL_CHAIN_ID; + /// @notice The maximum payment token amount for a single range payment, constrained by off-chain calculation + uint256 internal constant MAX_PAYMENT_AMOUNT = 1e38 - 1; + + /// @dev Index for flag that pauses payForRange payments + uint8 internal constant PAUSED_PAY_FOR_RANGE = 0; + /// @dev Index for flag that pauses payAllForRange payments + uint8 internal constant PAUSED_PAY_ALL_FOR_RANGE = 1; + /// @dev Index for flag that pauses + uint8 internal constant PAUSED_CLAIM_PAYMENTS = 2; + /// @dev Index for flag that pauses submitRoots + uint8 internal constant PAUSED_SUBMIT_ROOTS = 3; + + /// @dev Salt for the earner leaf, meant to distinguish from tokenLeaf since they have the same sized data + uint8 internal constant EARNER_LEAF_SALT = 0; + /// @dev Salt for the token leaf, meant to distinguish from earnerLeaf since they have the same sized data + uint8 internal constant TOKEN_LEAF_SALT = 1; + + /// @notice Canonical, virtual beacon chain ETH strategy + IStrategy public constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); + + modifier onlyPaymentUpdater() { + require(msg.sender == paymentUpdater, "PaymentCoordinator: caller is not the paymentUpdater"); + _; + } + + modifier onlyPayAllForRangeSubmitter() { + require( + isPayAllForRangeSubmitter[msg.sender], + "PaymentCoordinator: caller is not a valid payAllForRange submitter" + ); + _; + } + + /// @dev Sets the immutable variables for the contract + constructor( + IDelegationManager _delegationManager, + IStrategyManager _strategyManager, + uint32 _CALCULATION_INTERVAL_SECONDS, + uint32 _MAX_PAYMENT_DURATION, + uint32 _MAX_RETROACTIVE_LENGTH, + uint32 _MAX_FUTURE_LENGTH, + uint32 _GENESIS_PAYMENT_TIMESTAMP + ) + PaymentCoordinatorStorage( + _delegationManager, + _strategyManager, + _CALCULATION_INTERVAL_SECONDS, + _MAX_PAYMENT_DURATION, + _MAX_RETROACTIVE_LENGTH, + _MAX_FUTURE_LENGTH, + _GENESIS_PAYMENT_TIMESTAMP + ) + { + _disableInitializers(); + ORIGINAL_CHAIN_ID = block.chainid; + } + + /** + * @dev Initializes the addresses of the initial owner, pauser registry, paymentUpdater and + * configures the initial paused status, activationDelay, and globalOperatorCommissionBips. + */ + function initialize( + address initialOwner, + IPauserRegistry _pauserRegistry, + uint256 initialPausedStatus, + address _paymentUpdater, + uint32 _activationDelay, + uint16 _globalCommissionBips + ) external initializer { + _DOMAIN_SEPARATOR = _calculateDomainSeparator(); + _initializePauser(_pauserRegistry, initialPausedStatus); + _transferOwnership(initialOwner); + _setPaymentUpdater(_paymentUpdater); + _setActivationDelay(_activationDelay); + _setGlobalOperatorCommission(_globalCommissionBips); + } + + /******************************************************************************* + EXTERNAL FUNCTIONS + *******************************************************************************/ + + /** + * @notice Creates a new range payment on behalf of an AVS, to be split amongst the + * set of stakers delegated to operators who are registered to the `avs` + * @param rangePayments The range payments being created + * @dev Expected to be called by the ServiceManager of the AVS on behalf of which the payment is being made + * @dev The duration of the `rangePayment` cannot exceed `MAX_PAYMENT_DURATION` + * @dev The tokens are sent to the `PaymentCoordinator` contract + * @dev Strategies must be in ascending order of addresses to check for duplicates + * @dev This function will revert if the `rangePayment` is malformed, + * e.g. if the `strategies` and `weights` arrays are of non-equal lengths + */ + function payForRange( + RangePayment[] calldata rangePayments + ) external onlyWhenNotPaused(PAUSED_PAY_FOR_RANGE) nonReentrant { + for (uint256 i = 0; i < rangePayments.length; i++) { + RangePayment calldata rangePayment = rangePayments[i]; + uint256 nonce = paymentNonce[msg.sender]; + bytes32 rangePaymentHash = keccak256(abi.encode(msg.sender, nonce, rangePayment)); + require( + !isRangePaymentHash[msg.sender][rangePaymentHash], + "PaymentCoordinator._payForRange: range payment hash already submitted" + ); + + _validateRangePayment(rangePayment); + + isRangePaymentHash[msg.sender][rangePaymentHash] = true; + paymentNonce[msg.sender] = nonce + 1; + + emit RangePaymentCreated(msg.sender, nonce, rangePaymentHash, rangePayment); + rangePayment.token.safeTransferFrom(msg.sender, address(this), rangePayment.amount); + } + } + + /** + * @notice similar to `payForRange` except the payment is split amongst *all* stakers + * rather than just those delegated to operators who are registered to a single avs and is + * a permissioned call based on isPayAllForRangeSubmitter mapping. + * @param rangePayments The range payments being created + */ + function payAllForRange( + RangePayment[] calldata rangePayments + ) external onlyWhenNotPaused(PAUSED_PAY_ALL_FOR_RANGE) onlyPayAllForRangeSubmitter nonReentrant { + for (uint256 i = 0; i < rangePayments.length; i++) { + RangePayment calldata rangePayment = rangePayments[i]; + uint256 nonce = paymentNonce[msg.sender]; + bytes32 rangePaymentForAllHash = keccak256(abi.encode(msg.sender, nonce, rangePayment)); + require( + !isRangePaymentForAllHash[msg.sender][rangePaymentForAllHash], + "PaymentCoordinator._payForRange: range payment hash already submitted" + ); + + _validateRangePayment(rangePayment); + + isRangePaymentForAllHash[msg.sender][rangePaymentForAllHash] = true; + paymentNonce[msg.sender] = nonce + 1; + + emit RangePaymentForAllCreated(msg.sender, nonce, rangePaymentForAllHash, rangePayment); + rangePayment.token.safeTransferFrom(msg.sender, address(this), rangePayment.amount); + } + } + + /** + * @notice Claim payments against a given root (read from distributionRoots[claim.rootIndex]). + * Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for, + * they can simply claim against the latest root and the contract will calculate the difference between + * their cumulativeEarnings and cumulativeClaimed. This difference is then transferred to recipient address. + * @param claim The PaymentMerkleClaim to be processed. + * Contains the root index, earner, payment leaves, and required proofs + * @param recipient The address recipient that receives the ERC20 payments + * @dev only callable by the valid claimer, that is + * if claimerFor[claim.earner] is address(0) then only the earner can claim, otherwise only + * claimerFor[claim.earner] can claim the payments. + */ + function processClaim( + PaymentMerkleClaim calldata claim, + address recipient + ) external onlyWhenNotPaused(PAUSED_CLAIM_PAYMENTS) nonReentrant { + DistributionRoot memory root = distributionRoots[claim.rootIndex]; + _checkClaim(claim, root); + // If claimerFor earner is not set, claimer is by default the earner. Else set to claimerFor + address earner = claim.earnerLeaf.earner; + address claimer = claimerFor[earner]; + if (claimer == address(0)) { + claimer = earner; + } + require(msg.sender == claimer, "PaymentCoordinator.processClaim: caller is not valid claimer"); + for (uint256 i = 0; i < claim.tokenIndices.length; i++) { + TokenTreeMerkleLeaf calldata tokenLeaf = claim.tokenLeaves[i]; + + uint256 currCumulativeClaimed = cumulativeClaimed[earner][tokenLeaf.token]; + require( + tokenLeaf.cumulativeEarnings > currCumulativeClaimed, + "PaymentCoordinator.processClaim: cumulativeEarnings must be gt than cumulativeClaimed" + ); + + // Calculate amount to claim and update cumulativeClaimed + uint256 claimAmount = tokenLeaf.cumulativeEarnings - currCumulativeClaimed; + cumulativeClaimed[earner][tokenLeaf.token] = tokenLeaf.cumulativeEarnings; + + tokenLeaf.token.safeTransfer(recipient, claimAmount); + emit PaymentClaimed(root.root, earner, claimer, recipient, tokenLeaf.token, claimAmount); + } + } + + /** + * @notice Creates a new distribution root. activatedAt is set to block.timestamp + activationDelay + * @param root The merkle root of the distribution + * @param paymentCalculationEndTimestamp The timestamp until which payments have been calculated + * @dev Only callable by the paymentUpdater + */ + function submitRoot( + bytes32 root, + uint32 paymentCalculationEndTimestamp + ) external onlyWhenNotPaused(PAUSED_SUBMIT_ROOTS) onlyPaymentUpdater { + require( + paymentCalculationEndTimestamp > currPaymentCalculationEndTimestamp, + "PaymentCoordinator.submitRoot: new root must be for newer calculated period" + ); + require( + paymentCalculationEndTimestamp < block.timestamp, + "PaymentCoordinator.submitRoot: paymentCalculationEndTimestamp cannot be in the future" + ); + uint32 rootIndex = uint32(distributionRoots.length); + uint32 activatedAt = uint32(block.timestamp) + activationDelay; + distributionRoots.push( + DistributionRoot({ + root: root, + activatedAt: activatedAt, + paymentCalculationEndTimestamp: paymentCalculationEndTimestamp + }) + ); + currPaymentCalculationEndTimestamp = paymentCalculationEndTimestamp; + emit DistributionRootSubmitted(rootIndex, root, paymentCalculationEndTimestamp, activatedAt); + } + + /** + * @notice Sets the address of the entity that can call `processClaim` on behalf of the earner (msg.sender) + * @param claimer The address of the entity that can call `processClaim` on behalf of the earner + * @dev Only callable by the `earner` + */ + function setClaimerFor(address claimer) external { + address earner = msg.sender; + address prevClaimer = claimerFor[earner]; + claimerFor[earner] = claimer; + emit ClaimerForSet(earner, prevClaimer, claimer); + } + + /** + * @notice Sets the delay in timestamp before a posted root can be claimed against + * @dev Only callable by the contract owner + * @param _activationDelay The new value for activationDelay + */ + function setActivationDelay(uint32 _activationDelay) external onlyOwner { + _setActivationDelay(_activationDelay); + } + + /** + * @notice Sets the global commission for all operators across all avss + * @dev Only callable by the contract owner + * @param _globalCommissionBips The commission for all operators across all avss + */ + function setGlobalOperatorCommission(uint16 _globalCommissionBips) external onlyOwner { + _setGlobalOperatorCommission(_globalCommissionBips); + } + + /** + * @notice Sets the permissioned `paymentUpdater` address which can post new roots + * @dev Only callable by the contract owner + * @param _paymentUpdater The address of the new paymentUpdater + */ + function setPaymentUpdater(address _paymentUpdater) external onlyOwner { + _setPaymentUpdater(_paymentUpdater); + } + + /** + * @notice Sets the permissioned `payAllForRangeSubmitter` address which can submit payAllForRange + * @dev Only callable by the contract owner + * @param _submitter The address of the payAllForRangeSubmitter + * @param _newValue The new value for isPayAllForRangeSubmitter + */ + function setPayAllForRangeSubmitter(address _submitter, bool _newValue) external onlyOwner { + bool prevValue = isPayAllForRangeSubmitter[_submitter]; + emit PayAllForRangeSubmitterSet(_submitter, prevValue, _newValue); + isPayAllForRangeSubmitter[_submitter] = _newValue; + } + + /******************************************************************************* + INTERNAL FUNCTIONS + *******************************************************************************/ + + /** + * @notice Validate a RangePayment. Called from both `payForRange` and `payAllForRange` + */ + function _validateRangePayment(RangePayment calldata rangePayment) internal view { + require(rangePayment.strategiesAndMultipliers.length > 0, "PaymentCoordinator._payForRange: no strategies set"); + require(rangePayment.amount > 0, "PaymentCoordinator._payForRange: amount cannot be 0"); + require(rangePayment.amount <= MAX_PAYMENT_AMOUNT, "PaymentCoordinator._payForRange: amount too large"); + require( + rangePayment.duration <= MAX_PAYMENT_DURATION, + "PaymentCoordinator._payForRange: duration exceeds MAX_PAYMENT_DURATION" + ); + require( + rangePayment.duration % CALCULATION_INTERVAL_SECONDS == 0, + "PaymentCoordinator._payForRange: duration must be a multiple of CALCULATION_INTERVAL_SECONDS" + ); + require( + rangePayment.startTimestamp % CALCULATION_INTERVAL_SECONDS == 0, + "PaymentCoordinator._payForRange: startTimestamp must be a multiple of CALCULATION_INTERVAL_SECONDS" + ); + require( + block.timestamp - MAX_RETROACTIVE_LENGTH <= rangePayment.startTimestamp && + GENESIS_PAYMENT_TIMESTAMP <= rangePayment.startTimestamp, + "PaymentCoordinator._payForRange: startTimestamp too far in the past" + ); + require( + rangePayment.startTimestamp <= block.timestamp + MAX_FUTURE_LENGTH, + "PaymentCoordinator._payForRange: startTimestamp too far in the future" + ); + + // Require rangePayment is for whitelisted strategy or beaconChainETHStrategy + address currAddress = address(0); + for (uint256 i = 0; i < rangePayment.strategiesAndMultipliers.length; ++i) { + IStrategy strategy = rangePayment.strategiesAndMultipliers[i].strategy; + require( + strategyManager.strategyIsWhitelistedForDeposit(strategy) || strategy == beaconChainETHStrategy, + "PaymentCoordinator._payForRange: invalid strategy considered" + ); + require( + currAddress < address(strategy), + "PaymentCoordinator._payForRange: strategies must be in ascending order to handle duplicates" + ); + currAddress = address(strategy); + } + } + + function _checkClaim(PaymentMerkleClaim calldata claim, DistributionRoot memory root) internal view { + require(block.timestamp >= root.activatedAt, "PaymentCoordinator._checkClaim: root not activated yet"); + require( + claim.tokenIndices.length == claim.tokenTreeProofs.length, + "PaymentCoordinator._checkClaim: tokenIndices and tokenProofs length mismatch" + ); + require( + claim.tokenTreeProofs.length == claim.tokenLeaves.length, + "PaymentCoordinator._checkClaim: tokenTreeProofs and leaves length mismatch" + ); + + // Verify inclusion of earners leaf (earner, earnerTokenRoot) in the distribution root + _verifyEarnerClaimProof({ + root: root.root, + earnerLeafIndex: claim.earnerIndex, + earnerProof: claim.earnerTreeProof, + earnerLeaf: claim.earnerLeaf + }); + // For each of the tokenLeaf proofs, verify inclusion of token tree leaf again the earnerTokenRoot + for (uint256 i = 0; i < claim.tokenIndices.length; ++i) { + _verifyTokenClaimProof({ + earnerTokenRoot: claim.earnerLeaf.earnerTokenRoot, + tokenLeafIndex: claim.tokenIndices[i], + tokenProof: claim.tokenTreeProofs[i], + tokenLeaf: claim.tokenLeaves[i] + }); + } + } + + /** + * @notice verify inclusion of the token claim proof in the earner token root hash (earnerTokenRoot). + * The token leaf comprises of the IERC20 token and cumulativeAmount of earnings. + * @param earnerTokenRoot root hash of the earner token subtree + * @param tokenLeafIndex index of the token leaf + * @param tokenProof proof of the token leaf in the earner token subtree + * @param tokenLeaf token leaf to be verified + */ + function _verifyTokenClaimProof( + bytes32 earnerTokenRoot, + uint32 tokenLeafIndex, + bytes calldata tokenProof, + TokenTreeMerkleLeaf calldata tokenLeaf + ) internal pure { + // Validate index size so that there aren't multiple valid indices for the given proof + // index can't be greater than 2**(tokenProof/32) + require( + tokenLeafIndex < (1 << (tokenProof.length / 32)), + "PaymentCoordinator._verifyTokenClaim: invalid tokenLeafIndex" + ); + + // Verify inclusion of token leaf + bytes32 tokenLeafHash = calculateTokenLeafHash(tokenLeaf); + require( + Merkle.verifyInclusionKeccak({ + root: earnerTokenRoot, + index: tokenLeafIndex, + proof: tokenProof, + leaf: tokenLeafHash + }), + "PaymentCoordinator._verifyTokenClaim: invalid token claim proof" + ); + } + + /** + * @notice verify inclusion of earner claim proof in the distribution root. This verifies + * the inclusion of the earner and earnerTokenRoot hash in the tree. The token claims are proven separately + * against the earnerTokenRoot hash (see _verifyTokenClaimProof). The earner leaf comprises of (earner, earnerTokenRoot) + * @param root distribution root that should be read from storage + * @param earnerLeafIndex index of the earner leaf + * @param earnerProof proof of the earners account root in the merkle tree + * @param earnerLeaf leaf of earner merkle tree containing the earner address and earner's token root hash + */ + function _verifyEarnerClaimProof( + bytes32 root, + uint32 earnerLeafIndex, + bytes calldata earnerProof, + EarnerTreeMerkleLeaf calldata earnerLeaf + ) internal pure { + // Validate index size so that there aren't multiple valid indices for the given proof + // index can't be greater than 2**(earnerProof/32) + require( + earnerLeafIndex < (1 << (earnerProof.length / 32)), + "PaymentCoordinator._verifyEarnerClaimProof: invalid earnerLeafIndex" + ); + // Verify inclusion of earner leaf + bytes32 earnerLeafHash = calculateEarnerLeafHash(earnerLeaf); + require( + Merkle.verifyInclusionKeccak({ + root: root, + index: earnerLeafIndex, + proof: earnerProof, + leaf: earnerLeafHash + }), + "PaymentCoordinator._verifyEarnerClaimProof: invalid earner claim proof" + ); + } + + function _setActivationDelay(uint32 _activationDelay) internal { + emit ActivationDelaySet(activationDelay, _activationDelay); + activationDelay = _activationDelay; + } + + function _setGlobalOperatorCommission(uint16 _globalCommissionBips) internal { + emit GlobalCommissionBipsSet(globalOperatorCommissionBips, _globalCommissionBips); + globalOperatorCommissionBips = _globalCommissionBips; + } + + function _setPaymentUpdater(address _paymentUpdater) internal { + emit PaymentUpdaterSet(paymentUpdater, _paymentUpdater); + paymentUpdater = _paymentUpdater; + } + + /******************************************************************************* + VIEW FUNCTIONS + *******************************************************************************/ + + /// @notice return the hash of the earner's leaf + function calculateEarnerLeafHash(EarnerTreeMerkleLeaf calldata leaf) public pure returns (bytes32) { + return keccak256(abi.encodePacked(EARNER_LEAF_SALT, leaf.earner, leaf.earnerTokenRoot)); + } + + /// @notice returns the hash of the earner's token leaf + function calculateTokenLeafHash(TokenTreeMerkleLeaf calldata leaf) public pure returns (bytes32) { + return keccak256(abi.encodePacked(TOKEN_LEAF_SALT, leaf.token, leaf.cumulativeEarnings)); + } + + /// @notice returns 'true' if the claim would currently pass the check in `processClaims` + /// but will revert if not valid + function checkClaim(PaymentMerkleClaim calldata claim) public view returns (bool) { + _checkClaim(claim, distributionRoots[claim.rootIndex]); + return true; + } + + /// @notice the commission for a specific operator for a specific avs + /// NOTE: Currently unused and simply returns the globalOperatorCommissionBips value but will be used in future release + function operatorCommissionBips(address operator, address avs) external view returns (uint16) { + return globalOperatorCommissionBips; + } + + function getDistributionRootsLength() public view returns (uint256) { + return distributionRoots.length; + } + + /// @notice loop through distribution roots from reverse and return hash + function getRootIndexFromHash(bytes32 rootHash) public view returns (uint32) { + for (uint32 i = uint32(distributionRoots.length); i > 0; i--) { + if (distributionRoots[i - 1].root == rootHash) { + return i - 1; + } + } + revert("PaymentCoordinator.getRootIndexFromHash: root not found"); + } + + /** + * @notice Getter function for the current EIP-712 domain separator for this contract. + * + * @dev The domain separator will change in the event of a fork that changes the ChainID. + * @dev By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision. + * for more detailed information please read EIP-712. + */ + function domainSeparator() public view returns (bytes32) { + if (block.chainid == ORIGINAL_CHAIN_ID) { + return _DOMAIN_SEPARATOR; + } else { + return _calculateDomainSeparator(); + } + } + + /** + * @dev Recalculates the domain separator when the chainid changes due to a fork. + */ + function _calculateDomainSeparator() internal view returns (bytes32) { + return keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes("EigenLayer")), block.chainid, address(this))); + } +} diff --git a/src/contracts/core/PaymentCoordinatorStorage.sol b/src/contracts/core/PaymentCoordinatorStorage.sol new file mode 100644 index 000000000..8b5ee9b2f --- /dev/null +++ b/src/contracts/core/PaymentCoordinatorStorage.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "../interfaces/IAVSDirectory.sol"; +import "../interfaces/IStrategyManager.sol"; +import "../interfaces/IDelegationManager.sol"; +import "../interfaces/IEigenPodManager.sol"; +import "../interfaces/IPaymentCoordinator.sol"; + +/** + * @title Storage variables for the `PaymentCoordinator` contract. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice This storage contract is separate from the logic to simplify the upgrade process. + */ +abstract contract PaymentCoordinatorStorage is IPaymentCoordinator { + + /******************************************************************************* + CONSTANTS AND IMMUTABLES + *******************************************************************************/ + + /// @notice The interval in seconds at which the calculation for range payment distribution is done. + /// @dev Payment durations must be multiples of this interval. This is going to be configured to 1 week + uint32 public immutable CALCULATION_INTERVAL_SECONDS; + /// @notice The maximum amount of time (seconds) that a range payment can span over + uint32 public immutable MAX_PAYMENT_DURATION; + /// @notice max amount of time (seconds) that a payment can start in the past + uint32 public immutable MAX_RETROACTIVE_LENGTH; + /// @notice max amount of time (seconds) that a payment can start in the future + uint32 public immutable MAX_FUTURE_LENGTH; + /// @notice absolute min timestamp (seconds) that a payment can start at + uint32 public immutable GENESIS_PAYMENT_TIMESTAMP; + /// @notice The cadence at which a snapshot is taken offchain for calculating payment distributions + uint32 internal constant SNAPSHOT_CADENCE = 1 days; + + /// @notice The DelegationManager contract for EigenLayer + IDelegationManager public immutable delegationManager; + + /// @notice The StrategyManager contract for EigenLayer + IStrategyManager public immutable strategyManager; + + /******************************************************************************* + STORAGE + *******************************************************************************/ + + /** + * @notice Original EIP-712 Domain separator for this contract. + * @dev The domain separator may change in the event of a fork that modifies the ChainID. + * Use the getter function `domainSeparator` to get the current domain separator for this contract. + */ + bytes32 internal _DOMAIN_SEPARATOR; + + /// @notice list of roots submitted by the paymentUpdater + DistributionRoot[] public distributionRoots; + + /// Slot 3 + /// @notice The address of the entity that can update the contract with new merkle roots + address public paymentUpdater; + /// @notice Delay in timestamp (seconds) before a posted root can be claimed against + uint32 public activationDelay; + /// @notice Timestamp for last submitted DistributionRoot + uint32 public currPaymentCalculationEndTimestamp; + + /// Slot 4 + /// @notice the commission for all operators across all avss + uint16 public globalOperatorCommissionBips; + + /// @notice Mapping: earner => the address of the entity who can call `processClaim` on behalf of the earner + mapping(address => address) public claimerFor; + + /// @notice Mapping: earner => token => total amount claimed + mapping(address => mapping(IERC20 => uint256)) public cumulativeClaimed; + + /// @notice Used for unique rangePaymentHashes per AVS and for PayAllForRangeSubmitters + mapping(address => uint256) public paymentNonce; + /// @notice Mapping: avs => rangePaymentHash => bool to check if range payment hash has been submitted + mapping(address => mapping(bytes32 => bool)) public isRangePaymentHash; + /// @notice Mapping: avs => rangePaymentForALlHash => bool to check if range payment hash for all has been submitted + mapping(address => mapping(bytes32 => bool)) public isRangePaymentForAllHash; + /// @notice Mapping: address => bool to check if the address is permissioned to submit payAllForRange + mapping(address => bool) public isPayAllForRangeSubmitter; + + constructor( + IDelegationManager _delegationManager, + IStrategyManager _strategyManager, + uint32 _CALCULATION_INTERVAL_SECONDS, + uint32 _MAX_PAYMENT_DURATION, + uint32 _MAX_RETROACTIVE_LENGTH, + uint32 _MAX_FUTURE_LENGTH, + uint32 _GENESIS_PAYMENT_TIMESTAMP + ) { + require( + _GENESIS_PAYMENT_TIMESTAMP % _CALCULATION_INTERVAL_SECONDS == 0, + "PaymentCoordinator: GENESIS_PAYMENT_TIMESTAMP must be a multiple of CALCULATION_INTERVAL_SECONDS" + ); + require( + _CALCULATION_INTERVAL_SECONDS % SNAPSHOT_CADENCE == 0, + "PaymentCoordinator: CALCULATION_INTERVAL_SECONDS must be a multiple of SNAPSHOT_CADENCE" + ); + delegationManager = _delegationManager; + strategyManager = _strategyManager; + CALCULATION_INTERVAL_SECONDS = _CALCULATION_INTERVAL_SECONDS; + MAX_PAYMENT_DURATION = _MAX_PAYMENT_DURATION; + MAX_RETROACTIVE_LENGTH = _MAX_RETROACTIVE_LENGTH; + MAX_FUTURE_LENGTH = _MAX_FUTURE_LENGTH; + GENESIS_PAYMENT_TIMESTAMP = _GENESIS_PAYMENT_TIMESTAMP; + } + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[40] private __gap; +} diff --git a/src/contracts/interfaces/IDelegationManager.sol b/src/contracts/interfaces/IDelegationManager.sol index 5b663d4e8..01dc0fbec 100644 --- a/src/contracts/interfaces/IDelegationManager.sol +++ b/src/contracts/interfaces/IDelegationManager.sol @@ -395,6 +395,9 @@ interface IDelegationManager is ISignatureUtils { */ function strategyWithdrawalDelayBlocks(IStrategy strategy) external view returns (uint256); + /// @notice return address of the beaconChainETHStrategy + function beaconChainETHStrategy() external view returns (IStrategy); + /** * @notice Calculates the digestHash for a `staker` to sign to delegate to an `operator` * @param staker The signing staker diff --git a/src/contracts/interfaces/IPaymentCoordinator.sol b/src/contracts/interfaces/IPaymentCoordinator.sol new file mode 100644 index 000000000..0c029df47 --- /dev/null +++ b/src/contracts/interfaces/IPaymentCoordinator.sol @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "./IStrategy.sol"; +import "./IPauserRegistry.sol"; + +/** + * @title Interface for the `IPaymentCoordinator` contract. + * @author Layr Labs, Inc. + * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service + * @notice Allows AVSs to make "Range Payments", which get distributed amongst the AVSs' confirmed + * Operators and the Stakers delegated to those Operators. + * Calculations are performed based on the completed Range Payments, with the results posted in + * a Merkle root against which Stakers & Operators can make claims. + */ +interface IPaymentCoordinator { + /// STRUCTS /// + /** + * @notice A linear combination of strategies and multipliers for AVSs to weigh + * EigenLayer strategies. + * @param strategy The EigenLayer strategy to be used for the payment + * @param multiplier The weight of the strategy in the payment + */ + struct StrategyAndMultiplier { + IStrategy strategy; + uint96 multiplier; + } + + /** + * Sliding Window for valid RangePayment startTimestamp + * + * Scenario A: GENESIS_PAYMENT_TIMESTAMP IS WITHIN RANGE + * <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH---> + * <--------------------valid range for startTimestamp------------------------> + * ^ + * GENESIS_PAYMENT_TIMESTAMP + * + * + * Scenario B: GENESIS_PAYMENT_TIMESTAMP IS OUT OF RANGE + * <-----MAX_RETROACTIVE_LENGTH-----> t (block.timestamp) <---MAX_FUTURE_LENGTH---> + * <------------------------valid range for startTimestamp------------------------> + * ^ + * GENESIS_PAYMENT_TIMESTAMP + * @notice RangePayment struct submitted by AVSs when making payments to their operators and stakers + * RangePayment can be for a time range within the valid window for startTimestamp and must be within max duration. + * See `payForRange()` for more details. + * @param strategiesAndMultipliers The strategies and their relative weights + * cannot have duplicate strategies and need to be sorted in ascending address order + * @param token The payment token to be distributed + * @param amount The total amount of tokens to be distributed + * @param startTimestamp The timestamp (seconds) at which the payment range is considered for distribution + * could start in the past or in the future but within a valid range. See the diagram above. + * @param duration The duration of the payment range in seconds. Must be <= MAX_PAYMENT_DURATION + */ + struct RangePayment { + StrategyAndMultiplier[] strategiesAndMultipliers; + IERC20 token; + uint256 amount; + uint32 startTimestamp; + uint32 duration; + } + + /** + * @notice A distribution root is a merkle root of the distribution of earnings for a given period. + * The PaymentCoordinator stores all historical distribution roots so that earners can claim their earnings against older roots + * if they wish but the merkle tree contains the cumulative earnings of all earners and tokens for a given period so earners (or their claimers if set) + * only need to claim against the latest root to claim all available earnings. + * @param root The merkle root of the distribution + * @param paymentCalculationEndTimestamp The timestamp (seconds) until which payments have been calculated + * @param activatedAt The timestamp (seconds) at which the root can be claimed against + */ + struct DistributionRoot { + bytes32 root; + uint32 paymentCalculationEndTimestamp; + uint32 activatedAt; + } + + /** + * @notice Internal leaf in the merkle tree for the earner's account leaf + * @param earner The address of the earner + * @param earnerTokenRoot The merkle root of the earner's token subtree + * Each leaf in the earner's token subtree is a TokenTreeMerkleLeaf + */ + + struct EarnerTreeMerkleLeaf { + address earner; + bytes32 earnerTokenRoot; + } + + /** + * @notice The actual leaves in the distribution merkle tree specifying the token earnings + * for the respective earner's subtree. Each leaf is a claimable amount of a token for an earner. + * @param token The token for which the earnings are being claimed + * @param cumulativeEarnings The cumulative earnings of the earner for the token + */ + struct TokenTreeMerkleLeaf { + IERC20 token; + uint256 cumulativeEarnings; + } + + /** + * @notice A claim against a distribution root called by an + * earners claimer (could be the earner themselves). Each token claim will claim the difference + * between the cumulativeEarnings of the earner and the cumulativeClaimed of the claimer. + * Each claim can specify which of the earner's earned tokens they want to claim. + * See `processClaim()` for more details. + * @param rootIndex The index of the root in the list of DistributionRoots + * @param earnerIndex The index of the earner's account root in the merkle tree + * @param earnerTreeProof The proof of the earner's EarnerTreeMerkleLeaf against the merkle root + * @param earnerLeaf The earner's EarnerTreeMerkleLeaf struct, providing the earner address and earnerTokenRoot + * @param tokenIndices The indices of the token leaves in the earner's subtree + * @param tokenTreeProofs The proofs of the token leaves against the earner's earnerTokenRoot + * @param tokenLeaves The token leaves to be claimed + * @dev The merkle tree is structured with the merkle root at the top and EarnerTreeMerkleLeaf as internal leaves + * in the tree. Each earner leaf has its own subtree with TokenTreeMerkleLeaf as leaves in the subtree. + * To prove a claim against a specified rootIndex(which specifies the distributionRoot being used), + * the claim will first verify inclusion of the earner leaf in the tree against distributionRoots[rootIndex].root. + * Then for each token, it will verify inclusion of the token leaf in the earner's subtree against the earner's earnerTokenRoot. + */ + struct PaymentMerkleClaim { + uint32 rootIndex; + uint32 earnerIndex; + bytes earnerTreeProof; + EarnerTreeMerkleLeaf earnerLeaf; + uint32[] tokenIndices; + bytes[] tokenTreeProofs; + TokenTreeMerkleLeaf[] tokenLeaves; + } + + /// EVENTS /// + + /// @notice emitted when an AVS creates a valid RangePayment + event RangePaymentCreated( + address indexed avs, + uint256 indexed paymentNonce, + bytes32 indexed rangePaymentHash, + RangePayment rangePayment + ); + /// @notice emitted when a valid RangePayment is created for all stakers by a valid submitter + event RangePaymentForAllCreated( + address indexed submitter, + uint256 indexed paymentNonce, + bytes32 indexed rangePaymentHash, + RangePayment rangePayment + ); + /// @notice paymentUpdater is responsible for submiting DistributionRoots, only owner can set paymentUpdater + event PaymentUpdaterSet(address indexed oldPaymentUpdater, address indexed newPaymentUpdater); + event PayAllForRangeSubmitterSet( + address indexed payAllForRangeSubmitter, + bool indexed oldValue, + bool indexed newValue + ); + event ActivationDelaySet(uint32 oldActivationDelay, uint32 newActivationDelay); + event GlobalCommissionBipsSet(uint16 oldGlobalCommissionBips, uint16 newGlobalCommissionBips); + event ClaimerForSet(address indexed earner, address indexed oldClaimer, address indexed claimer); + /// @notice rootIndex is the specific array index of the newly created root in the storage array + event DistributionRootSubmitted( + uint32 indexed rootIndex, + bytes32 indexed root, + uint32 indexed paymentCalculationEndTimestamp, + uint32 activatedAt + ); + /// @notice root is one of the submitted distribution roots that was claimed against + event PaymentClaimed( + bytes32 root, + address indexed earner, + address indexed claimer, + address indexed recipient, + IERC20 token, + uint256 claimedAmount + ); + + /******************************************************************************* + VIEW FUNCTIONS + *******************************************************************************/ + + /// @notice The address of the entity that can update the contract with new merkle roots + function paymentUpdater() external view returns (address); + + /** + * @notice The interval in seconds at which the calculation for range payment distribution is done. + * @dev Payment durations must be multiples of this interval. + */ + function CALCULATION_INTERVAL_SECONDS() external view returns (uint32); + + /// @notice The maximum amount of time (seconds) that a range payment can span over + function MAX_PAYMENT_DURATION() external view returns (uint32); + + /// @notice max amount of time (seconds) that a payment can start in the past + function MAX_RETROACTIVE_LENGTH() external view returns (uint32); + + /// @notice max amount of time (seconds) that a payment can start in the future + function MAX_FUTURE_LENGTH() external view returns (uint32); + + /// @notice absolute min timestamp (seconds) that a payment can start at + function GENESIS_PAYMENT_TIMESTAMP() external view returns (uint32); + + /// @notice Delay in timestamp (seconds) before a posted root can be claimed against + function activationDelay() external view returns (uint32); + + /// @notice Mapping: earner => the address of the entity who can call `processClaim` on behalf of the earner + function claimerFor(address earner) external view returns (address); + + /// @notice Mapping: claimer => token => total amount claimed + function cumulativeClaimed(address claimer, IERC20 token) external view returns (uint256); + + /// @notice the commission for all operators across all avss + function globalOperatorCommissionBips() external view returns (uint16); + + /// @notice the commission for a specific operator for a specific avs + /// NOTE: Currently unused and simply returns the globalOperatorCommissionBips value but will be used in future release + function operatorCommissionBips(address operator, address avs) external view returns (uint16); + + /// @notice return the hash of the earner's leaf + function calculateEarnerLeafHash(EarnerTreeMerkleLeaf calldata leaf) external pure returns (bytes32); + + /// @notice returns the hash of the earner's token leaf + function calculateTokenLeafHash(TokenTreeMerkleLeaf calldata leaf) external pure returns (bytes32); + + /// @notice returns 'true' if the claim would currently pass the check in `processClaims` + /// but will revert if not valid + function checkClaim(PaymentMerkleClaim calldata claim) external view returns (bool); + + /// @notice The timestamp until which payments have been calculated + function currPaymentCalculationEndTimestamp() external view returns (uint32); + + /// @notice loop through distribution roots from reverse and return hash + function getRootIndexFromHash(bytes32 rootHash) external view returns (uint32); + + /******************************************************************************* + EXTERNAL FUNCTIONS + *******************************************************************************/ + + /** + * @notice Creates a new range payment on behalf of an AVS, to be split amongst the + * set of stakers delegated to operators who are registered to the `avs` + * @param rangePayments The range payments being created + * @dev Expected to be called by the ServiceManager of the AVS on behalf of which the payment is being made + * @dev The duration of the `rangePayment` cannot exceed `MAX_PAYMENT_DURATION` + * @dev The tokens are sent to the `PaymentCoordinator` contract + * @dev Strategies must be in ascending order of addresses to check for duplicates + * @dev This function will revert if the `rangePayment` is malformed, + * e.g. if the `strategies` and `weights` arrays are of non-equal lengths + */ + function payForRange(RangePayment[] calldata rangePayments) external; + + /** + * @notice similar to `payForRange` except the payment is split amongst *all* stakers + * rather than just those delegated to operators who are registered to a single avs and is + * a permissioned call based on isPayAllForRangeSubmitter mapping. + */ + function payAllForRange(RangePayment[] calldata rangePayment) external; + + /** + * @notice Claim payments against a given root (read from distributionRoots[claim.rootIndex]). + * Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for, + * they can simply claim against the latest root and the contract will calculate the difference between + * their cumulativeEarnings and cumulativeClaimed. This difference is then transferred to recipient address. + * @param claim The PaymentMerkleClaim to be processed. + * Contains the root index, earner, payment leaves, and required proofs + * @param recipient The address recipient that receives the ERC20 payments + * @dev only callable by the valid claimer, that is + * if claimerFor[claim.earner] is address(0) then only the earner can claim, otherwise only + * claimerFor[claim.earner] can claim the payments. + */ + function processClaim(PaymentMerkleClaim calldata claim, address recipient) external; + + /** + * @notice Creates a new distribution root. activatedAt is set to block.timestamp + activationDelay + * @param root The merkle root of the distribution + * @param paymentCalculationEndTimestamp The timestamp (seconds) until which payments have been calculated + * @dev Only callable by the paymentUpdater + */ + function submitRoot(bytes32 root, uint32 paymentCalculationEndTimestamp) external; + + /** + * @notice Sets the address of the entity that can call `processClaim` on behalf of the earner (msg.sender) + * @param claimer The address of the entity that can claim payments on behalf of the earner + * @dev Only callable by the `earner` + */ + function setClaimerFor(address claimer) external; + + /** + * @notice Sets the delay in timestamp before a posted root can be claimed against + * @param _activationDelay Delay in timestamp (seconds) before a posted root can be claimed against + * @dev Only callable by the contract owner + */ + function setActivationDelay(uint32 _activationDelay) external; + + /** + * @notice Sets the global commission for all operators across all avss + * @param _globalCommissionBips The commission for all operators across all avss + * @dev Only callable by the contract owner + */ + function setGlobalOperatorCommission(uint16 _globalCommissionBips) external; + + /** + * @notice Sets the permissioned `paymentUpdater` address which can post new roots + * @dev Only callable by the contract owner + */ + function setPaymentUpdater(address _paymentUpdater) external; + + /** + * @notice Sets the permissioned `payAllForRangeSubmitter` address which can submit payAllForRange + * @dev Only callable by the contract owner + * @param _submitter The address of the payAllForRangeSubmitter + * @param _newValue The new value for isPayAllForRangeSubmitter + */ + function setPayAllForRangeSubmitter(address _submitter, bool _newValue) external; +} diff --git a/src/contracts/interfaces/IStrategyManager.sol b/src/contracts/interfaces/IStrategyManager.sol index ecdb175c8..84632dfce 100644 --- a/src/contracts/interfaces/IStrategyManager.sol +++ b/src/contracts/interfaces/IStrategyManager.sol @@ -127,6 +127,9 @@ interface IStrategyManager { /// @notice Returns the address of the `strategyWhitelister` function strategyWhitelister() external view returns (address); + /// @notice Returns bool for whether or not `strategy` is whitelisted for deposit + function strategyIsWhitelistedForDeposit(IStrategy strategy) external view returns (bool); + /** * @notice Returns bool for whether or not `strategy` enables credit transfers. i.e enabling * depositIntoStrategyWithSignature calls or queueing withdrawals to a different address than the staker. diff --git a/src/contracts/libraries/Merkle.sol b/src/contracts/libraries/Merkle.sol index ddb0bd4c5..b8bb7fe68 100644 --- a/src/contracts/libraries/Merkle.sol +++ b/src/contracts/libraries/Merkle.sol @@ -40,6 +40,7 @@ library Merkle { * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. The tree is built assuming `leaf` is * the 0 indexed `index`'th leaf from the bottom left of the tree. + * @dev If the proof length is 0 then the leaf hash is returned. * * _Available since v4.4._ * @@ -51,8 +52,8 @@ library Merkle { uint256 index ) internal pure returns (bytes32) { require( - proof.length != 0 && proof.length % 32 == 0, - "Merkle.processInclusionProofKeccak: proof length should be a non-zero multiple of 32" + proof.length % 32 == 0, + "Merkle.processInclusionProofKeccak: proof length should be a multiple of 32" ); bytes32 computedHash = leaf; for (uint256 i = 32; i <= proof.length; i += 32) { diff --git a/src/test/EigenPod.t.sol b/src/test/EigenPod.t.sol index eb703d088..306a386d5 100644 --- a/src/test/EigenPod.t.sol +++ b/src/test/EigenPod.t.sol @@ -1256,29 +1256,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { Merkle.verifyInclusionSha256(proof, root, leaf, index); } - /// @notice Test that the Merkle proof verification fails when the proof length is empty - function testVerifyInclusionKeccakFailsForEmptyProof(bytes32 root, bytes32 leaf, uint256 index) public { - bytes memory emptyProof = new bytes(0); - cheats.expectRevert( - bytes("Merkle.processInclusionProofKeccak: proof length should be a non-zero multiple of 32") - ); - Merkle.verifyInclusionKeccak(emptyProof, root, leaf, index); - } - - /// @notice Test that the Merkle proof verification fails when the proof length is not a multiple of 32 - function testVerifyInclusionKeccakFailsForNonMultipleOf32ProofLength( - bytes32 root, - bytes32 leaf, - uint256 index, - bytes memory proof - ) public { - cheats.assume(proof.length % 32 != 0); - cheats.expectRevert( - bytes("Merkle.processInclusionProofKeccak: proof length should be a non-zero multiple of 32") - ); - Merkle.verifyInclusionKeccak(proof, root, leaf, index); - } - // verifies that the `numPod` variable increments correctly on a succesful call to the `EigenPod.stake` function function test_incrementNumPodsOnStake( bytes calldata _pubkey, diff --git a/src/test/events/IPaymentCoordinatorEvents.sol b/src/test/events/IPaymentCoordinatorEvents.sol new file mode 100644 index 000000000..a1b6b2377 --- /dev/null +++ b/src/test/events/IPaymentCoordinatorEvents.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "src/contracts/interfaces/IPaymentCoordinator.sol"; + +interface IPaymentCoordinatorEvents { + /// EVENTS /// + + /// @notice emitted when an AVS creates a valid RangePayment + event RangePaymentCreated( + address indexed avs, + uint256 indexed paymentNonce, + bytes32 indexed rangePaymentHash, + IPaymentCoordinator.RangePayment rangePayment + ); + /// @notice emitted when a valid RangePayment is created for all stakers by a valid submitter + event RangePaymentForAllCreated( + address indexed submitter, + uint256 indexed paymentNonce, + bytes32 indexed rangePaymentHash, + IPaymentCoordinator.RangePayment rangePayment + ); + /// @notice paymentUpdater is responsible for submiting DistributionRoots, only owner can set paymentUpdater + event PaymentUpdaterSet(address indexed oldPaymentUpdater, address indexed newPaymentUpdater); + event PayAllForRangeSubmitterSet( + address indexed payAllForRangeSubmitter, + bool indexed oldValue, + bool indexed newValue + ); + event ActivationDelaySet(uint32 oldActivationDelay, uint32 newActivationDelay); + event CalculationIntervalSecondsSet(uint32 oldCalculationIntervalSeconds, uint32 newCalculationIntervalSeconds); + event GlobalCommissionBipsSet(uint16 oldGlobalCommissionBips, uint16 newGlobalCommissionBips); + event ClaimerForSet(address indexed earner, address indexed oldClaimer, address indexed claimer); + /// @notice rootIndex is the specific array index of the newly created root in the storage array + event DistributionRootSubmitted( + uint32 indexed rootIndex, + bytes32 indexed root, + uint32 indexed paymentCalculationEndTimestamp, + uint32 activatedAt + ); + /// @notice root is one of the submitted distribution roots that was claimed against + event PaymentClaimed( + bytes32 root, + address indexed earner, + address indexed claimer, + address indexed recipient, + IERC20 token, + uint256 claimedAmount + ); + + + + /// TOKEN EVENTS FOR TESTING /// + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/src/test/mocks/DelegationManagerMock.sol b/src/test/mocks/DelegationManagerMock.sol index cd6ba51eb..3538b9b79 100644 --- a/src/test/mocks/DelegationManagerMock.sol +++ b/src/test/mocks/DelegationManagerMock.sol @@ -79,6 +79,9 @@ contract DelegationManagerMock is IDelegationManager, Test { return 0; } + /// @notice return address of the beaconChainETHStrategy + function beaconChainETHStrategy() external view returns (IStrategy) {} + /** * @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner, * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). diff --git a/src/test/mocks/StrategyManagerMock.sol b/src/test/mocks/StrategyManagerMock.sol index e9ccf28c6..f7ba6e901 100644 --- a/src/test/mocks/StrategyManagerMock.sol +++ b/src/test/mocks/StrategyManagerMock.sol @@ -30,6 +30,8 @@ contract StrategyManagerMock is mapping(address => IStrategy[]) public strategiesToReturn; mapping(address => uint256[]) public sharesToReturn; + mapping(IStrategy => bool) public strategyIsWhitelistedForDeposit; + /// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated. only increments (doesn't decrement) mapping(address => uint256) public cumulativeWithdrawalsQueued; @@ -105,6 +107,11 @@ contract StrategyManagerMock is stakerStrategyListLengthReturnValue = valueToSet; } + function setStrategyWhitelist(IStrategy strategy, bool value) external { + strategyIsWhitelistedForDeposit[strategy] = value; + } + + function removeShares(address staker, IStrategy strategy, uint256 shares) external {} function addShares(address staker, IERC20 token, IStrategy strategy, uint256 shares) external {} diff --git a/src/test/test-data/paymentCoordinator/processClaimProofs_MaxEarnerAndLeafIndices.json b/src/test/test-data/paymentCoordinator/processClaimProofs_MaxEarnerAndLeafIndices.json new file mode 100644 index 000000000..89de1b304 --- /dev/null +++ b/src/test/test-data/paymentCoordinator/processClaimProofs_MaxEarnerAndLeafIndices.json @@ -0,0 +1,24 @@ +{ + "Root": "0x37550707c80f3d8907c467999730e52127ab89be3f17a5017a3f1ffb73a1445f", + "RootIndex": 0, + "EarnerIndex": 7, + "EarnerTreeProof": "0x4bf5e16eaabbc36964f1e1639808669420f55d60e51adb7e9695b77145c479fd6777be59643947bb24d78e69d6605bf369c515b479f3a8967dd68a97c5bb4a4a262b28002eeb6cbbffb7e79e5741bf2be189a6073440a62fabcd8af4dbda94e3", + "EarnerLeaf": { + "Earner": "0x25a1b7322f9796b26a4bec125913b34c292b28d6", + "EarnerTokenRoot": "0xf8e7e20b32aae1d818dcb593b98982841e9a0ed12c161ad603e3ee3948746cba" + }, + "LeafIndices": [ + 7 + ], + "TokenTreeProofs": [ + "0x3cd04e8fc6f23812c570fe12292a30bb9e105e00f5913ac4b4938f23e65d8d10e6b1403d58c9d5450952e7d96c81305dad9fb966e8a27d3a42058e3958a0d30033148e91b455542d05deb81b8305b672e742cd3145f7022a0089bad2e6af9173" + ], + "TokenLeaves": [ + { + "Token": "0x7fbfdd1dfd80730385aee232cc9f79b8ae12a654", + "CumulativeEarnings": 3000000000000000000 + } + ], + "TokenTreeProofsNum": 1, + "TokenLeavesNum": 1 +} \ No newline at end of file diff --git a/src/test/test-data/paymentCoordinator/processClaimProofs_Root1.json b/src/test/test-data/paymentCoordinator/processClaimProofs_Root1.json new file mode 100644 index 000000000..b620619c2 --- /dev/null +++ b/src/test/test-data/paymentCoordinator/processClaimProofs_Root1.json @@ -0,0 +1,54 @@ +{ + "Root": "0xc5d6bb1073f9040366851b5971493165893558f1cdc0b0046b6703baa85cddfc", + "RootIndex": 0, + "EarnerIndex": 3, + "EarnerTreeProof": "0x04da796e892869089dfdaae7269c8eb12548f6aa3774a747322b65c42d1ca0050d38d6d5e7e9e65ba24c275fcc0f0c377d8fb6ed089770d849c39a1829d1edf27e78a529aa8f867a3777f97541a23fb1844d6ae24c3b8ca1cc981510e5d08bda", + "EarnerLeaf": { + "Earner": "0xf2288d736d27c1584ebf7be5f52f9e4d47251aee", + "EarnerTokenRoot": "0xa81e82a39e6da197c3c83b8b1343eb7e8a969db52d4bfc424fd04d60350d76e3" + }, + "LeafIndices": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "TokenTreeProofs": [ + "0x6166682a9a29283a51a1c1575de82334227cc45b1ce686973039a44eb9a6b008e3e64bd80597510ac0b737fc645f15801cc3835b279412e1a09ba66d50c2aa82e3f64b05b0f9ee23e853d9c134984c27b5b58d14b70c4dacea9a5e40600e17a3", + "0x167f18d55815451f979244946b7eb2ce019c323a9e02fba1b2e05e19a27b91b5e3e64bd80597510ac0b737fc645f15801cc3835b279412e1a09ba66d50c2aa82e3f64b05b0f9ee23e853d9c134984c27b5b58d14b70c4dacea9a5e40600e17a3", + "0x2faa07574e263370227b938c72ca18ae40edf215067ce325ebfc36f11b1f19484923b477146618e0f36993536e7bbec8ef5346613df2fb9d53caf8d9365b4c68e3f64b05b0f9ee23e853d9c134984c27b5b58d14b70c4dacea9a5e40600e17a3", + "0x4dccc729db7b3ad40fc9acfe257d45196427285382332a4bc6704e1ae42785474923b477146618e0f36993536e7bbec8ef5346613df2fb9d53caf8d9365b4c68e3f64b05b0f9ee23e853d9c134984c27b5b58d14b70c4dacea9a5e40600e17a3", + "0x1e9348730aee854752d72b32f4eed96ad80093807b53f3a07068536ce96d0e9aad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb51ab784a49394ced1b194ec2cac2163a6a5a0108e31a2510f82162f3d41b79962", + "0x7a570fedf4656f3240f44fb4771c946ea688c554f82e204778b792013b29ded3ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb51ab784a49394ced1b194ec2cac2163a6a5a0108e31a2510f82162f3d41b79962" + ], + "TokenLeaves": [ + { + "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", + "CumulativeEarnings": 1000000000000000000 + }, + { + "Token": "0x11a4b85eab283c98d27c8ae64469224d55ed1894", + "CumulativeEarnings": 400000000000000000 + }, + { + "Token": "0x43afffbe0afacdabe9ce7dbc4f07407a2b788a84", + "CumulativeEarnings": 30000000000000000000 + }, + { + "Token": "0x748a3ed7e6b04239150d7ebe12d7aef3e3994a23", + "CumulativeEarnings": 250000000000 + }, + { + "Token": "0xd275b23e0a5b68ae251b0dc4c81104cba36e7cd6", + "CumulativeEarnings": 884300000000000000 + }, + { + "Token": "0xec562acb9e470de27dca2495950660fa9fbd85f8", + "CumulativeEarnings": 42000000000000 + } + ], + "TokenTreeProofsNum": 6, + "TokenLeavesNum": 6 +} \ No newline at end of file diff --git a/src/test/test-data/paymentCoordinator/processClaimProofs_Root2.json b/src/test/test-data/paymentCoordinator/processClaimProofs_Root2.json new file mode 100644 index 000000000..7ed67a1ae --- /dev/null +++ b/src/test/test-data/paymentCoordinator/processClaimProofs_Root2.json @@ -0,0 +1,54 @@ +{ + "Root": "0x4d58d15093f392176c29504b94be56fb4969cf100083b6e8d3c79373f2d25974", + "RootIndex": 0, + "EarnerIndex": 3, + "EarnerTreeProof": "0xa9ed93ecba6058bc5cfab91319b40d344e7c829fb40eb26208e2898cb90071ed3bcd0aa5a1fbb1782053d35aaec2c6203ff53f6d6493100bafbf9506c7a3edf2bbcc90923e3f236d88ec22330aa91f3f50c425bc56ad3ff9707c72dcf853e206", + "EarnerLeaf": { + "Earner": "0xf2288d736d27c1584ebf7be5f52f9e4d47251aee", + "EarnerTokenRoot": "0x6eeac100b8cd705b92cda3015844005d918e118a3c7ba20046b6523cdc203d48" + }, + "LeafIndices": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "TokenTreeProofs": [ + "0x57898ab69c4024d674e8c18eea33fef5cd76c5b01e0f93ef168c602298f7b27ee2b46f44bc74268bc383609078e0217a0ea21d6a658975171f5233c1cd3133c0797252a751b45d9f15ef34a230f200a1c8bb69cdbbfb1fead4a3fd0f792fda58", + "0x97a04c80233ddc54eccd67a57f442c63c8f741079f41d95dd6242e275dcbe871e2b46f44bc74268bc383609078e0217a0ea21d6a658975171f5233c1cd3133c0797252a751b45d9f15ef34a230f200a1c8bb69cdbbfb1fead4a3fd0f792fda58", + "0xb0f239391b892c4b6f4ce0fea0a15de171f977f6798713e540b3493b93b557ffb9cf5ff8530c7a7932e4349c3c4d0172466b77e791a4e8bb1523fdb57c1fe96a797252a751b45d9f15ef34a230f200a1c8bb69cdbbfb1fead4a3fd0f792fda58", + "0x71a2384092132151928d8a13f599e52ad0aebe5587aa1cac9199d782c08847e2b9cf5ff8530c7a7932e4349c3c4d0172466b77e791a4e8bb1523fdb57c1fe96a797252a751b45d9f15ef34a230f200a1c8bb69cdbbfb1fead4a3fd0f792fda58", + "0xf8b6a59d2da40bb3c9a9fed1f77336c45e3550f34b8bcb76fa64adcf6d9a409dad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb508231e83c23fa7d9b9a2820a400601f21f946161a3817010724592cf53cf4ad9", + "0x87fa7a4daeedebd0069679c39d095a21293b412f6f9fdae3398b454160703e3bad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb508231e83c23fa7d9b9a2820a400601f21f946161a3817010724592cf53cf4ad9" + ], + "TokenLeaves": [ + { + "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", + "CumulativeEarnings": 1630000000000000000 + }, + { + "Token": "0x11a4b85eab283c98d27c8ae64469224d55ed1894", + "CumulativeEarnings": 780000000000000000 + }, + { + "Token": "0x43afffbe0afacdabe9ce7dbc4f07407a2b788a84", + "CumulativeEarnings": 37800000000000000000 + }, + { + "Token": "0x748a3ed7e6b04239150d7ebe12d7aef3e3994a23", + "CumulativeEarnings": 5050000000000 + }, + { + "Token": "0xd275b23e0a5b68ae251b0dc4c81104cba36e7cd6", + "CumulativeEarnings": 2284300000000000000 + }, + { + "Token": "0xec562acb9e470de27dca2495950660fa9fbd85f8", + "CumulativeEarnings": 886400000000000 + } + ], + "TokenTreeProofsNum": 6, + "TokenLeavesNum": 6 +} \ No newline at end of file diff --git a/src/test/test-data/paymentCoordinator/processClaimProofs_Root3.json b/src/test/test-data/paymentCoordinator/processClaimProofs_Root3.json new file mode 100644 index 000000000..1827469b1 --- /dev/null +++ b/src/test/test-data/paymentCoordinator/processClaimProofs_Root3.json @@ -0,0 +1,54 @@ +{ + "Root": "0x53d76498642862250c1caa8b14df55642d977200467a3dfce62e6da30b4820c6", + "RootIndex": 0, + "EarnerIndex": 3, + "EarnerTreeProof": "0xecd8c0e6d2d221742f8025acd6f1f0a5ff8482fe8f1cb135439e346df6fd56acce4f8a04a8bcb37dcbc11cb6984ba73cfa4da51dd037f7be27c257cf0605673b38d2f9a729da60cd7c3c2b7ff53bebfdef61de0871518eb36654368ac584b6b8", + "EarnerLeaf": { + "Earner": "0xf2288d736d27c1584ebf7be5f52f9e4d47251aee", + "EarnerTokenRoot": "0x36c11c299ad4a8b95a795d6afc1f5f958b9d1a1ea5cc13ea2fc59b6ccd4b6ee4" + }, + "LeafIndices": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "TokenTreeProofs": [ + "0xfcf8546a323ba4d4bcfbdb5930b3bede1e93ec4bdd8d1372ed84db3126df143fb306484eeaece55c5cd8db0ce3f3a77f4aa6fa5150b95db06b9c1cec4825aae157a8dfba810f55acf97f229426649e26f6fe58886e1edcb404535334da43d92d", + "0xde4e83fd8b6b5e44f72fd511cd1a9ce6704f16378e5ca20c15f4d52efe27aa57b306484eeaece55c5cd8db0ce3f3a77f4aa6fa5150b95db06b9c1cec4825aae157a8dfba810f55acf97f229426649e26f6fe58886e1edcb404535334da43d92d", + "0x67f846894f21973d7db36e6b363141b291b2f0f57bfc4b9a74b4f5335c0d068a17aac423247fab5821f643830b49570dfdecd0f30a9ae24ff5fd59bd4e9e8a4e57a8dfba810f55acf97f229426649e26f6fe58886e1edcb404535334da43d92d", + "0xc27217da67ce7b0418f5c8bef4ceeefe04c5561ddd73ab9750798d8f981d981d17aac423247fab5821f643830b49570dfdecd0f30a9ae24ff5fd59bd4e9e8a4e57a8dfba810f55acf97f229426649e26f6fe58886e1edcb404535334da43d92d", + "0xd084816ad70c189dbfa8be4e6b1f912edb6fe3b39ba1c19c4ed16d91b84f903dad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb550d72c280c9bd978b5ef45c3647daa2ab564342c473cb977f35b053706ed19d1", + "0x2bec75e7737677b586943a2292393137cad6617ac248d249d220afb3bf31f4dcad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb550d72c280c9bd978b5ef45c3647daa2ab564342c473cb977f35b053706ed19d1" + ], + "TokenLeaves": [ + { + "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", + "CumulativeEarnings": 3500000000000000000 + }, + { + "Token": "0x11a4b85eab283c98d27c8ae64469224d55ed1894", + "CumulativeEarnings": 1240000000000000000 + }, + { + "Token": "0x43afffbe0afacdabe9ce7dbc4f07407a2b788a84", + "CumulativeEarnings": 56800000000000000000 + }, + { + "Token": "0x748a3ed7e6b04239150d7ebe12d7aef3e3994a23", + "CumulativeEarnings": 8900000000000 + }, + { + "Token": "0xd275b23e0a5b68ae251b0dc4c81104cba36e7cd6", + "CumulativeEarnings": 4343000000000000000 + }, + { + "Token": "0xec562acb9e470de27dca2495950660fa9fbd85f8", + "CumulativeEarnings": 1064000000000000 + } + ], + "TokenTreeProofsNum": 6, + "TokenLeavesNum": 6 +} \ No newline at end of file diff --git a/src/test/test-data/paymentCoordinator/processClaimProofs_SingleEarnerLeaf.json b/src/test/test-data/paymentCoordinator/processClaimProofs_SingleEarnerLeaf.json new file mode 100644 index 000000000..74cffb0dd --- /dev/null +++ b/src/test/test-data/paymentCoordinator/processClaimProofs_SingleEarnerLeaf.json @@ -0,0 +1,36 @@ +{ + "Root": "0xd4aa4d1bdb95eb78f061238d587609407301a05bd952334ad6b5e8ca60bb347e", + "RootIndex": 0, + "EarnerIndex": 0, + "EarnerTreeProof": "0x", + "EarnerLeaf": { + "Earner": "0x0d6ba28b9919cfcdb6b233469cc5ce30b979e08e", + "EarnerTokenRoot": "0x12104ce9deb6e62ef479656476be1da6d71905234252cf9a5c9733e6cb115d77" + }, + "LeafIndices": [ + 0, + 2, + 3 + ], + "TokenTreeProofs": [ + "0xf027f6a1f6522a5ede64f16448001378272bcc3b4d9b2660f241ea87d4cafc880d5fa8201813790dcfddb37966811813117ab78db2cce941c8b1d1f3888cccca6271c73f26f5e492cc990b25f85410beaf6f04958410162dc16eb5e3ce4791ce", + "0x9ae7a20b0f244cb25a1c18339432139587ecc0058130e9ebb10a98049981395d5bef23d3c6b22f7b1265e30a68e83b675394ea57b7fa150f5ecc07bae7e3b88d6271c73f26f5e492cc990b25f85410beaf6f04958410162dc16eb5e3ce4791ce", + "0x31335a07ff9047e08d712967b2ef1720f8682cb696d9df15902602b0c5eebdde5bef23d3c6b22f7b1265e30a68e83b675394ea57b7fa150f5ecc07bae7e3b88d6271c73f26f5e492cc990b25f85410beaf6f04958410162dc16eb5e3ce4791ce" + ], + "TokenLeaves": [ + { + "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", + "CumulativeEarnings": 1000000000000000000 + }, + { + "Token": "0x43afffbe0afacdabe9ce7dbc4f07407a2b788a84", + "CumulativeEarnings": 3000000000000000000 + }, + { + "Token": "0x748a3ed7e6b04239150d7ebe12d7aef3e3994a23", + "CumulativeEarnings": 1000000000000000000 + } + ], + "TokenTreeProofsNum": 3, + "TokenLeavesNum": 3 +} \ No newline at end of file diff --git a/src/test/test-data/paymentCoordinator/processClaimProofs_SingleTokenLeaf.json b/src/test/test-data/paymentCoordinator/processClaimProofs_SingleTokenLeaf.json new file mode 100644 index 000000000..36734380c --- /dev/null +++ b/src/test/test-data/paymentCoordinator/processClaimProofs_SingleTokenLeaf.json @@ -0,0 +1,24 @@ +{ + "Root": "0x86867b737e68a56280554c0447ac6b43ba1cb68f4a46d1d9a0ecb919f912959b", + "RootIndex": 0, + "EarnerIndex": 3, + "EarnerTreeProof": "0x04da796e892869089dfdaae7269c8eb12548f6aa3774a747322b65c42d1ca0050d38d6d5e7e9e65ba24c275fcc0f0c377d8fb6ed089770d849c39a1829d1edf27e78a529aa8f867a3777f97541a23fb1844d6ae24c3b8ca1cc981510e5d08bda", + "EarnerLeaf": { + "Earner": "0xf2288d736d27c1584ebf7be5f52f9e4d47251aee", + "EarnerTokenRoot": "0x167f18d55815451f979244946b7eb2ce019c323a9e02fba1b2e05e19a27b91b5" + }, + "LeafIndices": [ + 0 + ], + "TokenTreeProofs": [ + "0x" + ], + "TokenLeaves": [ + { + "Token": "0x1006dd1b8c3d0ef53489bed27577c75299f71473", + "CumulativeEarnings": 1000000000000000000 + } + ], + "TokenTreeProofsNum": 1, + "TokenLeavesNum": 1 +} \ No newline at end of file diff --git a/src/test/test-data/paymentCoordinator/processClaim_Preprod_Test.json b/src/test/test-data/paymentCoordinator/processClaim_Preprod_Test.json new file mode 100644 index 000000000..8c5701318 --- /dev/null +++ b/src/test/test-data/paymentCoordinator/processClaim_Preprod_Test.json @@ -0,0 +1,24 @@ +{ + "Root": "0x5e3227269ddcd5ddc7bc76ef42243a16166e86f9adab6183ee49b9875f2c7002", + "RootIndex": 0, + "EarnerIndex": 1, + "EarnerTreeProof": "0x468cfb9f23175803c35daccc788454e82e192aa69bd770b26861dbe1fb42336d44b3fc6ad9da7ba33382006677e1808659ccd50c35705233f0fedca7b34c0e07d8894c83317a23b1f54108d86045843d34a127d92fb262452a636d0d40b577e1", + "EarnerLeaf": { + "Earner": "0x2222aac0c980cc029624b7ff55b88bc6f63c538f", + "EarnerTokenRoot": "0x987aa019b1caf3fb0eaf02ebc1b8ce46840aee48d94ee21a2753045805ae38a8" + }, + "LeafIndices": [ + 0 + ], + "TokenTreeProofs": [ + "0x" + ], + "TokenLeaves": [ + { + "Token": "0x94373a4919b3240d86ea41593d5eba789fef3848", + "CumulativeEarnings": 1003827094673442500 + } + ], + "TokenTreeProofsNum": 1, + "TokenLeavesNum": 1 +} \ No newline at end of file diff --git a/src/test/unit/PaymentCoordinatorUnit.t.sol b/src/test/unit/PaymentCoordinatorUnit.t.sol new file mode 100644 index 000000000..ebb52799a --- /dev/null +++ b/src/test/unit/PaymentCoordinatorUnit.t.sol @@ -0,0 +1,2004 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts/mocks/ERC1271WalletMock.sol"; +import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; + +import "src/contracts/core/PaymentCoordinator.sol"; +import "src/contracts/strategies/StrategyBase.sol"; + +import "src/test/events/IPaymentCoordinatorEvents.sol"; +import "src/test/utils/EigenLayerUnitTestSetup.sol"; +import "src/test/mocks/Reenterer.sol"; +import "src/test/mocks/ERC20Mock.sol"; + +/** + * @notice Unit testing of the PaymentCoordinator contract + * Contracts tested: PaymentCoordinator + * Contracts not mocked: StrategyBase, PauserRegistry + */ +contract PaymentCoordinatorUnitTests is EigenLayerUnitTestSetup, IPaymentCoordinatorEvents { + // used for stack too deep + struct FuzzPayForRange { + address avs; + uint256 startTimestamp; + uint256 duration; + uint256 amount; + } + + // Contract under test + PaymentCoordinator public paymentCoordinator; + PaymentCoordinator public paymentCoordinatorImplementation; + + // Mocks + IERC20 token1; + IERC20 token2; + IERC20 token3; + IStrategy strategyMock1; + IStrategy strategyMock2; + IStrategy strategyMock3; + StrategyBase strategyImplementation; + uint256 mockTokenInitialSupply = 1e38 - 1; + IPaymentCoordinator.StrategyAndMultiplier[] defaultStrategyAndMultipliers; + + // Config Variables + /// @notice intervals(epochs) are 1 weeks + uint32 CALCULATION_INTERVAL_SECONDS = 7 days; + + /// @notice Max duration is 5 epochs (2 weeks * 5 = 10 weeks in seconds) + uint32 MAX_PAYMENT_DURATION = 70 days; + + /// @notice Lower bound start range is ~3 months into the past, multiple of CALCULATION_INTERVAL_SECONDS + uint32 MAX_RETROACTIVE_LENGTH = 84 days; + /// @notice Upper bound start range is ~1 month into the future, multiple of CALCULATION_INTERVAL_SECONDS + uint32 MAX_FUTURE_LENGTH = 28 days; + /// @notice absolute min timestamp that a payment can start at + uint32 GENESIS_PAYMENT_TIMESTAMP = 1712188800; + + /// @notice Delay in timestamp before a posted root can be claimed against + uint32 activationDelay = 7 days; + /// @notice the commission for all operators across all avss + uint16 globalCommissionBips = 1000; + + IERC20[] paymentTokens; + + // PaymentCoordinator Constants + + /// @dev Index for flag that pauses payForRange payments + uint8 internal constant PAUSED_PAY_FOR_RANGE = 0; + + /// @dev Index for flag that pauses payAllForRange payments + uint8 internal constant PAUSED_PAY_ALL_FOR_RANGE = 1; + + /// @dev Index for flag that pauses claiming + uint8 internal constant PAUSED_CLAIM_PAYMENTS = 2; + + /// @dev Index for flag that pauses submitRoots + uint8 internal constant PAUSED_SUBMIT_ROOTS = 3; + + // PaymentCoordinator entities + address paymentUpdater = address(1000); + address defaultAVS = address(1001); + address defaultClaimer = address(1002); + address payAllSubmitter = address(1003); + + function setUp() public virtual override { + // Setup + EigenLayerUnitTestSetup.setUp(); + + // Deploy PaymentCoordinator proxy and implementation + paymentCoordinatorImplementation = new PaymentCoordinator( + delegationManagerMock, + strategyManagerMock, + CALCULATION_INTERVAL_SECONDS, + MAX_PAYMENT_DURATION, + MAX_RETROACTIVE_LENGTH, + MAX_FUTURE_LENGTH, + GENESIS_PAYMENT_TIMESTAMP + ); + paymentCoordinator = PaymentCoordinator( + address( + new TransparentUpgradeableProxy( + address(paymentCoordinatorImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector( + PaymentCoordinator.initialize.selector, + address(this), // initOwner + pauserRegistry, + 0, // 0 is initialPausedStatus + paymentUpdater, + activationDelay, + globalCommissionBips + ) + ) + ) + ); + + // Deploy mock token and strategy + token1 = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, address(this)); + token2 = new ERC20PresetFixedSupply("jeo boden", "MOCK2", mockTokenInitialSupply, address(this)); + token3 = new ERC20PresetFixedSupply("pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this)); + + strategyImplementation = new StrategyBase(strategyManagerMock); + strategyMock1 = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(strategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, token1, pauserRegistry) + ) + ) + ); + strategyMock2 = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(strategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, token2, pauserRegistry) + ) + ) + ); + strategyMock3 = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(strategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, token3, pauserRegistry) + ) + ) + ); + IStrategy[] memory strategies = new IStrategy[](3); + strategies[0] = strategyMock1; + strategies[1] = strategyMock2; + strategies[2] = strategyMock3; + strategies = _sortArrayAsc(strategies); + + strategyManagerMock.setStrategyWhitelist(strategies[0], true); + strategyManagerMock.setStrategyWhitelist(strategies[1], true); + strategyManagerMock.setStrategyWhitelist(strategies[2], true); + + defaultStrategyAndMultipliers.push( + IPaymentCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) + ); + defaultStrategyAndMultipliers.push( + IPaymentCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) + ); + defaultStrategyAndMultipliers.push( + IPaymentCoordinator.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) + ); + + paymentCoordinator.setPayAllForRangeSubmitter(payAllSubmitter, true); + paymentCoordinator.setPaymentUpdater(paymentUpdater); + + // Exclude from fuzzed tests + addressIsExcludedFromFuzzedInputs[address(paymentCoordinator)] = true; + addressIsExcludedFromFuzzedInputs[address(paymentUpdater)] = true; + + // Set the timestamp to some time after the genesis payment timestamp + cheats.warp(GENESIS_PAYMENT_TIMESTAMP + 5 days); + } + + /// @notice deploy token to owner and approve paymentCoordinator. Used for deploying payment tokens + function _deployMockPaymentTokens(address owner, uint256 numTokens) internal virtual { + cheats.startPrank(owner); + for (uint256 i = 0; i < numTokens; ++i) { + IERC20 token = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, owner); + paymentTokens.push(token); + token.approve(address(paymentCoordinator), mockTokenInitialSupply); + } + cheats.stopPrank(); + } + + function _getBalanceForTokens(IERC20[] memory tokens, address holder) internal view returns (uint256[] memory) { + uint256[] memory balances = new uint256[](tokens.length); + for (uint256 i = 0; i < tokens.length; ++i) { + balances[i] = tokens[i].balanceOf(holder); + } + return balances; + } + + function _maxTimestamp(uint32 timestamp1, uint32 timestamp2) internal pure returns (uint32) { + return timestamp1 > timestamp2 ? timestamp1 : timestamp2; + } + + function _assertPaymentClaimedEvents(bytes32 root, IPaymentCoordinator.PaymentMerkleClaim memory claim, address recipient) internal { + address earner = claim.earnerLeaf.earner; + address claimer = paymentCoordinator.claimerFor(earner); + if (claimer == address(0)) { + claimer = earner; + } + IERC20 token; + uint256 claimedAmount; + for (uint256 i = 0; i < claim.tokenLeaves.length; ++i) { + token = claim.tokenLeaves[i].token; + claimedAmount = paymentCoordinator.cumulativeClaimed(earner, token); + + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit PaymentClaimed( + root, + earner, + claimer, + recipient, + token, + claim.tokenLeaves[i].cumulativeEarnings - claimedAmount + ); + } + } + + /// @notice given address and array of payment tokens, return array of cumulativeClaimed amonts + function _getCumulativeClaimed( + address earner, + IPaymentCoordinator.PaymentMerkleClaim memory claim + ) internal view returns (uint256[] memory) { + uint256[] memory totalClaimed = new uint256[](claim.tokenLeaves.length); + + for (uint256 i = 0; i < claim.tokenLeaves.length; ++i) { + totalClaimed[i] = paymentCoordinator.cumulativeClaimed(earner, claim.tokenLeaves[i].token); + } + + return totalClaimed; + } + + /// @notice given a claim, return the new cumulativeEarnings for each token + function _getCumulativeEarnings( + IPaymentCoordinator.PaymentMerkleClaim memory claim + ) internal pure returns (uint256[] memory) { + uint256[] memory earnings = new uint256[](claim.tokenLeaves.length); + + for (uint256 i = 0; i < claim.tokenLeaves.length; ++i) { + earnings[i] = claim.tokenLeaves[i].cumulativeEarnings; + } + + return earnings; + } + + function _getClaimTokenBalances( + address earner, + IPaymentCoordinator.PaymentMerkleClaim memory claim + ) internal view returns (uint256[] memory) { + uint256[] memory balances = new uint256[](claim.tokenLeaves.length); + + for (uint256 i = 0; i < claim.tokenLeaves.length; ++i) { + balances[i] = claim.tokenLeaves[i].token.balanceOf(earner); + } + + return balances; + } + + /// @dev Sort to ensure that the array is in ascending order for strategies + function _sortArrayAsc(IStrategy[] memory arr) internal pure returns (IStrategy[] memory) { + uint256 l = arr.length; + for (uint256 i = 0; i < l; i++) { + for (uint256 j = i + 1; j < l; j++) { + if (address(arr[i]) > address(arr[j])) { + IStrategy temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + } + } + return arr; + } +} + +contract PaymentCoordinatorUnitTests_initializeAndSetters is PaymentCoordinatorUnitTests { + function testFuzz_setClaimerFor(address earner, address claimer) public filterFuzzedAddressInputs(earner) { + cheats.startPrank(earner); + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit ClaimerForSet(earner, paymentCoordinator.claimerFor(earner), claimer); + paymentCoordinator.setClaimerFor(claimer); + assertEq(claimer, paymentCoordinator.claimerFor(earner), "claimerFor not set"); + cheats.stopPrank(); + } + + function testFuzz_setActivationDelay(uint32 activationDelay) public { + cheats.startPrank(paymentCoordinator.owner()); + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit ActivationDelaySet(paymentCoordinator.activationDelay(), activationDelay); + paymentCoordinator.setActivationDelay(activationDelay); + assertEq(activationDelay, paymentCoordinator.activationDelay(), "activationDelay not set"); + cheats.stopPrank(); + } + + function testFuzz_setActivationDelay_Revert_WhenNotOwner( + address caller, + uint32 activationDelay + ) public filterFuzzedAddressInputs(caller) { + cheats.assume(caller != paymentCoordinator.owner()); + cheats.prank(caller); + cheats.expectRevert("Ownable: caller is not the owner"); + paymentCoordinator.setActivationDelay(activationDelay); + } + + function testFuzz_setGlobalOperatorCommission(uint16 globalCommissionBips) public { + cheats.startPrank(paymentCoordinator.owner()); + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit GlobalCommissionBipsSet(paymentCoordinator.globalOperatorCommissionBips(), globalCommissionBips); + paymentCoordinator.setGlobalOperatorCommission(globalCommissionBips); + assertEq( + globalCommissionBips, + paymentCoordinator.globalOperatorCommissionBips(), + "globalOperatorCommissionBips not set" + ); + cheats.stopPrank(); + } + + function testFuzz_setGlobalOperatorCommission_Revert_WhenNotOwner( + address caller, + uint16 globalCommissionBips + ) public filterFuzzedAddressInputs(caller) { + cheats.assume(caller != paymentCoordinator.owner()); + cheats.prank(caller); + cheats.expectRevert("Ownable: caller is not the owner"); + paymentCoordinator.setGlobalOperatorCommission(globalCommissionBips); + } + + function testFuzz_setPaymentUpdater(address newPaymentUpdater) public { + cheats.startPrank(paymentCoordinator.owner()); + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit PaymentUpdaterSet(paymentCoordinator.paymentUpdater(), newPaymentUpdater); + paymentCoordinator.setPaymentUpdater(newPaymentUpdater); + assertEq(newPaymentUpdater, paymentCoordinator.paymentUpdater(), "paymentUpdater not set"); + cheats.stopPrank(); + } + + function testFuzz_setPaymentUpdater_Revert_WhenNotOwner( + address caller, + address newPaymentUpdater + ) public filterFuzzedAddressInputs(caller) { + cheats.assume(caller != paymentCoordinator.owner()); + cheats.prank(caller); + cheats.expectRevert("Ownable: caller is not the owner"); + paymentCoordinator.setPaymentUpdater(newPaymentUpdater); + } + + function testFuzz_setPayAllForRangeSubmitter(address submitter, bool newValue) public { + cheats.startPrank(paymentCoordinator.owner()); + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit PayAllForRangeSubmitterSet(submitter, paymentCoordinator.isPayAllForRangeSubmitter(submitter), newValue); + paymentCoordinator.setPayAllForRangeSubmitter(submitter, newValue); + assertEq( + newValue, + paymentCoordinator.isPayAllForRangeSubmitter(submitter), + "isPayAllForRangeSubmitter not set" + ); + cheats.stopPrank(); + } + + function testFuzz_setPayAllForRangeSubmitter_Revert_WhenNotOwner( + address caller, + address submitter, + bool newValue + ) public filterFuzzedAddressInputs(caller) { + cheats.assume(caller != paymentCoordinator.owner()); + cheats.prank(caller); + cheats.expectRevert("Ownable: caller is not the owner"); + paymentCoordinator.setPayAllForRangeSubmitter(submitter, newValue); + } +} + +contract PaymentCoordinatorUnitTests_payForRange is PaymentCoordinatorUnitTests { + // Revert when paused + function test_Revert_WhenPaused() public { + cheats.prank(pauser); + paymentCoordinator.pause(2 ** PAUSED_PAY_FOR_RANGE); + + cheats.expectRevert("Pausable: index is paused"); + IPaymentCoordinator.RangePayment[] memory rangePayments; + paymentCoordinator.payForRange(rangePayments); + } + + // Revert from reentrancy + function test_Revert_WhenReentrancy(uint256 amount) public { + amount = bound(amount, 1, 1e38-1); + Reenterer reenterer = new Reenterer(); + + reenterer.prepareReturnData(abi.encode(amount)); + + address targetToUse = address(paymentCoordinator); + uint256 msgValueToUse = 0; + + _deployMockPaymentTokens(address(this), 1); + + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: IERC20(address(reenterer)), + amount: amount, + startTimestamp: uint32(block.timestamp), + duration: 0 + }); + + bytes memory calldataToUse = abi.encodeWithSelector(PaymentCoordinator.payForRange.selector, rangePayments); + reenterer.prepare(targetToUse, msgValueToUse, calldataToUse, bytes("ReentrancyGuard: reentrant call")); + + cheats.expectRevert(); + paymentCoordinator.payForRange(rangePayments); + } + + // Revert with 0 length strats and multipliers + function testFuzz_Revert_WhenEmptyStratsAndMultipliers( + address avs, + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public filterFuzzedAddressInputs(avs) { + cheats.assume(avs != address(0)); + cheats.prank(paymentCoordinator.owner()); + + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 paymentToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, avs); + amount = bound(amount, 1, mockTokenInitialSupply); + duration = bound(duration, 0, MAX_PAYMENT_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + IPaymentCoordinator.StrategyAndMultiplier[] memory emptyStratsAndMultipliers; + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: emptyStratsAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. call payForRange() with expected revert + cheats.prank(avs); + cheats.expectRevert("PaymentCoordinator._payForRange: no strategies set"); + paymentCoordinator.payForRange(rangePayments); + } + + // Revert when amount > 1e38-1 + function testFuzz_Revert_AmountTooLarge( + address avs, + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public filterFuzzedAddressInputs(avs) { + // 1. Bound fuzz inputs + amount = bound(amount, 1e38, type(uint256).max); + IERC20 paymentToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", amount, avs); + duration = bound(duration, 0, MAX_PAYMENT_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. Call payForRange() with expected revert + cheats.prank(avs); + cheats.expectRevert("PaymentCoordinator._payForRange: amount too large"); + paymentCoordinator.payForRange(rangePayments); + } + + function testFuzz_Revert_WhenDuplicateStrategies( + address avs, + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public filterFuzzedAddressInputs(avs) { + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 paymentToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, avs); + amount = bound(amount, 1, mockTokenInitialSupply); + duration = bound(duration, 0, MAX_PAYMENT_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + IPaymentCoordinator.StrategyAndMultiplier[] + memory dupStratsAndMultipliers = new IPaymentCoordinator.StrategyAndMultiplier[](2); + dupStratsAndMultipliers[0] = defaultStrategyAndMultipliers[0]; + dupStratsAndMultipliers[1] = defaultStrategyAndMultipliers[0]; + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: dupStratsAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. call payForRange() with expected revert + cheats.prank(avs); + cheats.expectRevert( + "PaymentCoordinator._payForRange: strategies must be in ascending order to handle duplicates" + ); + paymentCoordinator.payForRange(rangePayments); + } + + // Revert with exceeding max duration + function testFuzz_Revert_WhenExceedingMaxDuration( + address avs, + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public filterFuzzedAddressInputs(avs) { + cheats.assume(avs != address(0)); + cheats.prank(paymentCoordinator.owner()); + + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 paymentToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, avs); + amount = bound(amount, 1, mockTokenInitialSupply); + duration = bound(duration, MAX_PAYMENT_DURATION + 1, type(uint32).max); + startTimestamp = bound( + startTimestamp, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. call payForRange() with expected revert + cheats.prank(avs); + cheats.expectRevert("PaymentCoordinator._payForRange: duration exceeds MAX_PAYMENT_DURATION"); + paymentCoordinator.payForRange(rangePayments); + } + + // Revert with invalid interval seconds + function testFuzz_Revert_WhenInvalidIntervalSeconds( + address avs, + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public filterFuzzedAddressInputs(avs) { + cheats.assume(avs != address(0)); + cheats.prank(paymentCoordinator.owner()); + + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 paymentToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, avs); + amount = bound(amount, 1, mockTokenInitialSupply); + duration = bound(duration, 0, MAX_PAYMENT_DURATION); + cheats.assume(duration % CALCULATION_INTERVAL_SECONDS != 0); + startTimestamp = bound( + startTimestamp, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. call payForRange() with expected revert + cheats.prank(avs); + cheats.expectRevert( + "PaymentCoordinator._payForRange: duration must be a multiple of CALCULATION_INTERVAL_SECONDS" + ); + paymentCoordinator.payForRange(rangePayments); + } + + // Revert with retroactive payments enabled and set too far in past + // - either before genesis payment timestamp + // - before max retroactive length + function testFuzz_Revert_WhenPaymentTooStale( + uint256 fuzzBlockTimestamp, + address avs, + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public filterFuzzedAddressInputs(avs) { + cheats.assume(avs != address(0)); + cheats.prank(paymentCoordinator.owner()); + + // 1. Bound fuzz inputs to valid ranges and amounts + fuzzBlockTimestamp = bound(fuzzBlockTimestamp, uint256(MAX_RETROACTIVE_LENGTH), block.timestamp); + cheats.warp(fuzzBlockTimestamp); + + IERC20 paymentToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, avs); + amount = bound(amount, 1, mockTokenInitialSupply); + duration = bound(duration, 0, MAX_PAYMENT_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + 0, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) - 1 + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. call payForRange() with expected revert + cheats.prank(avs); + cheats.expectRevert("PaymentCoordinator._payForRange: startTimestamp too far in the past"); + paymentCoordinator.payForRange(rangePayments); + } + + // Revert with start timestamp past max future length + function testFuzz_Revert_WhenPaymentTooFarInFuture( + address avs, + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public filterFuzzedAddressInputs(avs) { + cheats.assume(avs != address(0)); + cheats.prank(paymentCoordinator.owner()); + + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 paymentToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, avs); + amount = bound(amount, 1, mockTokenInitialSupply); + duration = bound(duration, 0, MAX_PAYMENT_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + 1 + CALCULATION_INTERVAL_SECONDS, + type(uint32).max + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. call payForRange() with expected revert + cheats.prank(avs); + cheats.expectRevert("PaymentCoordinator._payForRange: startTimestamp too far in the future"); + paymentCoordinator.payForRange(rangePayments); + } + + // Revert with non whitelisted strategy + function testFuzz_Revert_WhenInvalidStrategy( + address avs, + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public filterFuzzedAddressInputs(avs) { + cheats.assume(avs != address(0)); + cheats.prank(paymentCoordinator.owner()); + + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 paymentToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, avs); + amount = bound(amount, 1, mockTokenInitialSupply); + duration = bound(duration, 0, MAX_PAYMENT_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + defaultStrategyAndMultipliers[0].strategy = IStrategy(address(999)); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. call payForRange() with expected event emitted + cheats.prank(avs); + cheats.expectRevert("PaymentCoordinator._payForRange: invalid strategy considered"); + paymentCoordinator.payForRange(rangePayments); + } + + /** + * @notice test a single range payment asserting for the following + * - correct event emitted + * - payment nonce incrementation by 1, and range payment hash being set in storage. + * - range payment hash being set in storage + * - token balance before and after of avs and paymentCoordinator + */ + function testFuzz_payForRange_SinglePayment( + address avs, + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public filterFuzzedAddressInputs(avs) { + cheats.assume(avs != address(0)); + cheats.prank(paymentCoordinator.owner()); + + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 paymentToken = new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, avs); + amount = bound(amount, 1, mockTokenInitialSupply); + duration = bound(duration, 0, MAX_PAYMENT_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. call payForRange() with expected event emitted + uint256 avsBalanceBefore = paymentToken.balanceOf(avs); + uint256 paymentCoordinatorBalanceBefore = paymentToken.balanceOf(address(paymentCoordinator)); + + cheats.startPrank(avs); + paymentToken.approve(address(paymentCoordinator), amount); + uint256 currPaymentNonce = paymentCoordinator.paymentNonce(avs); + bytes32 rangePaymentHash = keccak256(abi.encode(avs, currPaymentNonce, rangePayments[0])); + + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit RangePaymentCreated(avs, currPaymentNonce, rangePaymentHash, rangePayments[0]); + paymentCoordinator.payForRange(rangePayments); + cheats.stopPrank(); + + assertTrue(paymentCoordinator.isRangePaymentHash(avs, rangePaymentHash), "Range payment hash not submitted"); + assertEq(currPaymentNonce + 1, paymentCoordinator.paymentNonce(avs), "Payment nonce not incremented"); + assertEq( + avsBalanceBefore - amount, + paymentToken.balanceOf(avs), + "AVS balance not decremented by amount of range payment" + ); + assertEq( + paymentCoordinatorBalanceBefore + amount, + paymentToken.balanceOf(address(paymentCoordinator)), + "PaymentCoordinator balance not incremented by amount of range payment" + ); + } + + /** + * @notice test multiple range payments asserting for the following + * - correct event emitted + * - payment nonce incrementation by numPayments, and range payment hashes being set in storage. + * - range payment hash being set in storage + * - token balances before and after of avs and paymentCoordinator + */ + function testFuzz_payForRange_MultiplePayments( + FuzzPayForRange memory param, + uint256 numPayments + ) public filterFuzzedAddressInputs(param.avs) { + cheats.assume(2 <= numPayments && numPayments <= 10); + cheats.assume(param.avs != address(0)); + cheats.prank(paymentCoordinator.owner()); + + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](numPayments); + bytes32[] memory rangePaymentHashes = new bytes32[](numPayments); + uint256 startPaymentNonce = paymentCoordinator.paymentNonce(param.avs); + _deployMockPaymentTokens(param.avs, numPayments); + + uint256[] memory avsBalancesBefore = _getBalanceForTokens(paymentTokens, param.avs); + uint256[] memory paymentCoordinatorBalancesBefore = _getBalanceForTokens( + paymentTokens, + address(paymentCoordinator) + ); + uint256[] memory amounts = new uint256[](numPayments); + + // Create multiple range payments and their expected event + for (uint256 i = 0; i < numPayments; ++i) { + // 1. Bound fuzz inputs to valid ranges and amounts using randSeed for each + param.amount = bound(param.amount + i, 1, mockTokenInitialSupply); + amounts[i] = param.amount; + param.duration = bound(param.duration + i, 0, MAX_PAYMENT_DURATION); + param.duration = param.duration - (param.duration % CALCULATION_INTERVAL_SECONDS); + param.startTimestamp = bound( + param.startTimestamp + i, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + param.startTimestamp = param.startTimestamp - (param.startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment memory rangePayment = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentTokens[i], + amount: amounts[i], + startTimestamp: uint32(param.startTimestamp), + duration: uint32(param.duration) + }); + rangePayments[i] = rangePayment; + + // 3. expected event emitted for this rangePayment + rangePaymentHashes[i] = keccak256(abi.encode(param.avs, startPaymentNonce + i, rangePayments[i])); + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit RangePaymentCreated(param.avs, startPaymentNonce + i, rangePaymentHashes[i], rangePayments[i]); + } + + // 4. call payForRange() + cheats.prank(param.avs); + paymentCoordinator.payForRange(rangePayments); + + // 5. Check for paymentNonce() and rangePaymentHashes being set + assertEq( + startPaymentNonce + numPayments, + paymentCoordinator.paymentNonce(param.avs), + "Payment nonce not incremented properly" + ); + + for (uint256 i = 0; i < numPayments; ++i) { + assertTrue( + paymentCoordinator.isRangePaymentHash(param.avs, rangePaymentHashes[i]), + "Range payment hash not submitted" + ); + assertEq( + avsBalancesBefore[i] - amounts[i], + paymentTokens[i].balanceOf(param.avs), + "AVS balance not decremented by amount of range payment" + ); + assertEq( + paymentCoordinatorBalancesBefore[i] + amounts[i], + paymentTokens[i].balanceOf(address(paymentCoordinator)), + "PaymentCoordinator balance not incremented by amount of range payment" + ); + } + } +} + +contract PaymentCoordinatorUnitTests_payAllForRange is PaymentCoordinatorUnitTests { + // Revert when paused + function test_Revert_WhenPaused() public { + cheats.prank(pauser); + paymentCoordinator.pause(2 ** PAUSED_PAY_ALL_FOR_RANGE); + + cheats.expectRevert("Pausable: index is paused"); + IPaymentCoordinator.RangePayment[] memory rangePayments; + paymentCoordinator.payAllForRange(rangePayments); + } + + // Revert from reentrancy + function test_Revert_WhenReentrancy(uint256 amount) public { + Reenterer reenterer = new Reenterer(); + + reenterer.prepareReturnData(abi.encode(amount)); + + address targetToUse = address(paymentCoordinator); + uint256 msgValueToUse = 0; + + _deployMockPaymentTokens(address(this), 1); + + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: IERC20(address(reenterer)), + amount: amount, + startTimestamp: uint32(block.timestamp), + duration: 0 + }); + + bytes memory calldataToUse = abi.encodeWithSelector(PaymentCoordinator.payForRange.selector, rangePayments); + reenterer.prepare(targetToUse, msgValueToUse, calldataToUse, bytes("ReentrancyGuard: reentrant call")); + + cheats.prank(payAllSubmitter); + cheats.expectRevert(); + paymentCoordinator.payAllForRange(rangePayments); + } + + function testFuzz_Revert_WhenNotPayAllForRangeSubmitter( + address invalidSubmitter + ) public filterFuzzedAddressInputs(invalidSubmitter) { + cheats.assume(invalidSubmitter != payAllSubmitter); + + cheats.expectRevert("PaymentCoordinator: caller is not a valid payAllForRange submitter"); + IPaymentCoordinator.RangePayment[] memory rangePayments; + paymentCoordinator.payAllForRange(rangePayments); + } + + /** + * @notice test a single range payment asserting for the following + * - correct event emitted + * - payment nonce incrementation by 1, and range payment hash being set in storage. + * - range payment hash being set in storage + * - token balance before and after of payAllForRangeSubmitter and paymentCoordinator + */ + function testFuzz_payAllForRange_SinglePayment(uint256 startTimestamp, uint256 duration, uint256 amount) public { + cheats.prank(paymentCoordinator.owner()); + + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 paymentToken = new ERC20PresetFixedSupply( + "dog wif hat", + "MOCK1", + mockTokenInitialSupply, + payAllSubmitter + ); + amount = bound(amount, 1, mockTokenInitialSupply); + duration = bound(duration, 0, MAX_PAYMENT_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](1); + rangePayments[0] = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. call payForRange() with expected event emitted + uint256 submitterBalanceBefore = paymentToken.balanceOf(payAllSubmitter); + uint256 paymentCoordinatorBalanceBefore = paymentToken.balanceOf(address(paymentCoordinator)); + + cheats.startPrank(payAllSubmitter); + paymentToken.approve(address(paymentCoordinator), amount); + uint256 currPaymentNonce = paymentCoordinator.paymentNonce(payAllSubmitter); + bytes32 rangePaymentHash = keccak256(abi.encode(payAllSubmitter, currPaymentNonce, rangePayments[0])); + + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit RangePaymentForAllCreated(payAllSubmitter, currPaymentNonce, rangePaymentHash, rangePayments[0]); + paymentCoordinator.payAllForRange(rangePayments); + cheats.stopPrank(); + + assertTrue( + paymentCoordinator.isRangePaymentForAllHash(payAllSubmitter, rangePaymentHash), + "Range payment hash not submitted" + ); + assertEq( + currPaymentNonce + 1, + paymentCoordinator.paymentNonce(payAllSubmitter), + "Payment nonce not incremented" + ); + assertEq( + submitterBalanceBefore - amount, + paymentToken.balanceOf(payAllSubmitter), + "PayAllForRange Submitter balance not decremented by amount of range payment" + ); + assertEq( + paymentCoordinatorBalanceBefore + amount, + paymentToken.balanceOf(address(paymentCoordinator)), + "PaymentCoordinator balance not incremented by amount of range payment" + ); + } + + /** + * @notice test multiple range payments asserting for the following + * - correct event emitted + * - payment nonce incrementation by numPayments, and range payment hashes being set in storage. + * - range payment hash being set in storage + * - token balances before and after of payAllForRange submitter and paymentCoordinator + */ + function testFuzz_payAllForRange_MultiplePayments(FuzzPayForRange memory param, uint256 numPayments) public { + cheats.assume(2 <= numPayments && numPayments <= 10); + cheats.prank(paymentCoordinator.owner()); + + IPaymentCoordinator.RangePayment[] memory rangePayments = new IPaymentCoordinator.RangePayment[](numPayments); + bytes32[] memory rangePaymentHashes = new bytes32[](numPayments); + uint256 startPaymentNonce = paymentCoordinator.paymentNonce(payAllSubmitter); + _deployMockPaymentTokens(payAllSubmitter, numPayments); + + uint256[] memory submitterBalancesBefore = _getBalanceForTokens(paymentTokens, payAllSubmitter); + uint256[] memory paymentCoordinatorBalancesBefore = _getBalanceForTokens( + paymentTokens, + address(paymentCoordinator) + ); + uint256[] memory amounts = new uint256[](numPayments); + + // Create multiple range payments and their expected event + for (uint256 i = 0; i < numPayments; ++i) { + // 1. Bound fuzz inputs to valid ranges and amounts using randSeed for each + param.amount = bound(param.amount + i, 1, mockTokenInitialSupply); + amounts[i] = param.amount; + param.duration = bound(param.duration + i, 0, MAX_PAYMENT_DURATION); + param.duration = param.duration - (param.duration % CALCULATION_INTERVAL_SECONDS); + param.startTimestamp = bound( + param.startTimestamp + i, + uint256(_maxTimestamp(GENESIS_PAYMENT_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH)) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + param.startTimestamp = param.startTimestamp - (param.startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create range payment input param + IPaymentCoordinator.RangePayment memory rangePayment = IPaymentCoordinator.RangePayment({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: paymentTokens[i], + amount: amounts[i], + startTimestamp: uint32(param.startTimestamp), + duration: uint32(param.duration) + }); + rangePayments[i] = rangePayment; + + // 3. expected event emitted for this rangePayment + rangePaymentHashes[i] = keccak256(abi.encode(payAllSubmitter, startPaymentNonce + i, rangePayments[i])); + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit RangePaymentForAllCreated( + payAllSubmitter, + startPaymentNonce + i, + rangePaymentHashes[i], + rangePayments[i] + ); + } + + // 4. call payForRange() + cheats.prank(payAllSubmitter); + paymentCoordinator.payAllForRange(rangePayments); + + // 5. Check for paymentNonce() and rangePaymentHashes being set + assertEq( + startPaymentNonce + numPayments, + paymentCoordinator.paymentNonce(payAllSubmitter), + "Payment nonce not incremented properly" + ); + + for (uint256 i = 0; i < numPayments; ++i) { + assertTrue( + paymentCoordinator.isRangePaymentForAllHash(payAllSubmitter, rangePaymentHashes[i]), + "Range payment hash not submitted" + ); + assertEq( + submitterBalancesBefore[i] - amounts[i], + paymentTokens[i].balanceOf(payAllSubmitter), + "PayAllForRange Submitter balance not decremented by amount of range payment" + ); + assertEq( + paymentCoordinatorBalancesBefore[i] + amounts[i], + paymentTokens[i].balanceOf(address(paymentCoordinator)), + "PaymentCoordinator balance not incremented by amount of range payment" + ); + } + } +} + +contract PaymentCoordinatorUnitTests_submitRoot is PaymentCoordinatorUnitTests { + // only callable by paymentUpdater + function testFuzz_Revert_WhenNotPaymentUpdater( + address invalidPaymentUpdater + ) public filterFuzzedAddressInputs(invalidPaymentUpdater) { + cheats.prank(invalidPaymentUpdater); + + cheats.expectRevert("PaymentCoordinator: caller is not the paymentUpdater"); + paymentCoordinator.submitRoot(bytes32(0), 0); + } + + function test_Revert_WhenSubmitRootPaused() public { + cheats.prank(pauser); + paymentCoordinator.pause(2 ** PAUSED_SUBMIT_ROOTS); + + cheats.expectRevert("Pausable: index is paused"); + paymentCoordinator.submitRoot(bytes32(0), 0); + } + + /// @notice submits root with correct values and adds to root storage array + /// - checks activatedAt has added activationDelay + function testFuzz_submitRoot(bytes32 root, uint32 paymentCalculationEndTimestamp) public { + // fuzz avoiding overflows and valid activatedAt values + cheats.assume(paymentCoordinator.currPaymentCalculationEndTimestamp() < paymentCalculationEndTimestamp); + cheats.assume(paymentCalculationEndTimestamp < block.timestamp); + + uint32 expectedRootIndex = uint32(paymentCoordinator.getDistributionRootsLength()); + uint32 activatedAt = uint32(block.timestamp) + paymentCoordinator.activationDelay(); + + cheats.expectEmit(true, true, true, true, address(paymentCoordinator)); + emit DistributionRootSubmitted(expectedRootIndex, root, paymentCalculationEndTimestamp, activatedAt); + cheats.prank(paymentUpdater); + paymentCoordinator.submitRoot(root, paymentCalculationEndTimestamp); + + ( + bytes32 submittedRoot, + uint32 submittedPaymentCalculationEndTimestamp, + uint32 submittedActivatedAt + ) = paymentCoordinator.distributionRoots(expectedRootIndex); + + assertEq( + expectedRootIndex, + paymentCoordinator.getDistributionRootsLength() - 1, + "root not added to roots array" + ); + assertEq(activatedAt, submittedActivatedAt, "activatedAt not correct"); + assertEq(root, submittedRoot, "root not set"); + assertEq( + paymentCalculationEndTimestamp, + submittedPaymentCalculationEndTimestamp, + "paymentCalculationEndTimestamp not set" + ); + assertEq( + paymentCoordinator.currPaymentCalculationEndTimestamp(), + paymentCalculationEndTimestamp, + "currPaymentCalculationEndTimestamp not set" + ); + } + + /// @notice Submits multiple roots and checks root index from hash is correct + function testFuzz_getRootIndexFromHash(bytes32 root, uint16 numRoots, uint256 index) public { + numRoots = uint16(bound(numRoots, 1, 100)); + index = bound(index, 0, uint256(numRoots - 1)); + + bytes32[] memory roots = new bytes32[](numRoots); + cheats.startPrank(paymentUpdater); + for (uint16 i = 0; i < numRoots; ++i) { + roots[i] = keccak256(abi.encodePacked(root, i)); + + uint32 activationDelay = uint32(block.timestamp) + paymentCoordinator.activationDelay(); + paymentCoordinator.submitRoot(roots[i], uint32(block.timestamp - 1)); + cheats.warp(activationDelay); + } + cheats.stopPrank(); + + assertEq(index, paymentCoordinator.getRootIndexFromHash(roots[index]), "root index not found"); + } +} + +/// @notice Tests for sets of JSON data with different distribution roots +contract PaymentCoordinatorUnitTests_processClaim is PaymentCoordinatorUnitTests { + using stdStorage for StdStorage; + + /// @notice earner address used for proofs + address earner = 0xF2288D736d27C1584Ebf7be5f52f9E4d47251AeE; + + /// @notice mock token bytecode + bytes mockTokenBytecode; + + uint32 prevRootCalculationEndTimestamp; + + // Temp storage for managing stack in _parseProofData + bytes32 merkleRoot; + uint32 earnerIndex; + bytes earnerTreeProof; + address proofEarner; + bytes32 earnerTokenRoot; + + function setUp() public virtual override { + PaymentCoordinatorUnitTests.setUp(); + + // Create mock token to use bytecode later to etch + IERC20 mockToken = new ERC20Mock(); + mockTokenBytecode = address(mockToken).code; + } + + /// @notice Claim against latest submitted root, rootIndex 3 + /// Limit fuzz runs to speed up tests since these require reading from JSON + /// forge-config: default.fuzz.runs = 10 + function testFuzz_processClaim_LatestRoot( + bool setClaimerFor, + address claimerFor + ) public filterFuzzedAddressInputs(claimerFor) { + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofs(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[2]; + + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + uint256[] memory totalClaimedBefore = _getCumulativeClaimed(earner, claim); + uint256[] memory earnings = _getCumulativeEarnings(claim); + uint256[] memory tokenBalancesBefore = _getClaimTokenBalances(claimer, claim); + + _assertPaymentClaimedEvents(root, claim, claimer); + paymentCoordinator.processClaim(claim, claimer); + + uint256[] memory tokenBalancesAfter = _getClaimTokenBalances(claimer, claim); + + for (uint256 i = 0; i < totalClaimedBefore.length; ++i) { + assertEq( + earnings[i] - totalClaimedBefore[i], + tokenBalancesAfter[i] - tokenBalancesBefore[i], + "Token balance not incremented by earnings amount" + ); + } + + cheats.stopPrank(); + } + + /// @notice Claim against an old root that isn't the latest + /// forge-config: default.fuzz.runs = 10 + function testFuzz_processClaim_OldRoot( + bool setClaimerFor, + address claimerFor + ) public filterFuzzedAddressInputs(claimerFor) { + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofs(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[0]; + + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + uint256[] memory totalClaimedBefore = _getCumulativeClaimed(earner, claim); + uint256[] memory earnings = _getCumulativeEarnings(claim); + uint256[] memory tokenBalancesBefore = _getClaimTokenBalances(claimer, claim); + + _assertPaymentClaimedEvents(root, claim, claimer); + paymentCoordinator.processClaim(claim, claimer); + + uint256[] memory tokenBalancesAfter = _getClaimTokenBalances(claimer, claim); + + for (uint256 i = 0; i < totalClaimedBefore.length; ++i) { + assertEq( + earnings[i] - totalClaimedBefore[i], + tokenBalancesAfter[i] - tokenBalancesBefore[i], + "Token balance not incremented by earnings amount" + ); + } + + cheats.stopPrank(); + } + + /// @notice Claim against all roots in order, rootIndex 0, 1, 2 + /// forge-config: default.fuzz.runs = 10 + function testFuzz_processClaim_Sequential( + bool setClaimerFor, + address claimerFor + ) public filterFuzzedAddressInputs(claimerFor) { + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofs(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[0]; + + // 1. Claim against first root + { + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + uint256[] memory totalClaimedBefore = _getCumulativeClaimed(earner, claim); + uint256[] memory earnings = _getCumulativeEarnings(claim); + uint256[] memory tokenBalancesBefore = _getClaimTokenBalances(claimer, claim); + + _assertPaymentClaimedEvents(root, claim, claimer); + paymentCoordinator.processClaim(claim, claimer); + + uint256[] memory tokenBalancesAfter = _getClaimTokenBalances(claimer, claim); + + for (uint256 i = 0; i < totalClaimedBefore.length; ++i) { + assertEq( + earnings[i] - totalClaimedBefore[i], + tokenBalancesAfter[i] - tokenBalancesBefore[i], + "Token balance not incremented by earnings amount" + ); + } + + cheats.stopPrank(); + } + + // 2. Claim against second root + claim = claims[1]; + { + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + uint256[] memory totalClaimedBefore = _getCumulativeClaimed(earner, claim); + uint256[] memory earnings = _getCumulativeEarnings(claim); + uint256[] memory tokenBalancesBefore = _getClaimTokenBalances(claimer, claim); + + _assertPaymentClaimedEvents(root, claim, claimer); + paymentCoordinator.processClaim(claim, claimer); + + uint256[] memory tokenBalancesAfter = _getClaimTokenBalances(claimer, claim); + + for (uint256 i = 0; i < totalClaimedBefore.length; ++i) { + assertEq( + earnings[i] - totalClaimedBefore[i], + tokenBalancesAfter[i] - tokenBalancesBefore[i], + "Token balance not incremented by earnings amount" + ); + } + + cheats.stopPrank(); + } + + // 3. Claim against third and latest root + claim = claims[2]; + { + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + uint256[] memory totalClaimedBefore = _getCumulativeClaimed(earner, claim); + uint256[] memory earnings = _getCumulativeEarnings(claim); + uint256[] memory tokenBalancesBefore = _getClaimTokenBalances(claimer, claim); + + _assertPaymentClaimedEvents(root, claim, claimer); + paymentCoordinator.processClaim(claim, claimer); + + uint256[] memory tokenBalancesAfter = _getClaimTokenBalances(claimer, claim); + + for (uint256 i = 0; i < totalClaimedBefore.length; ++i) { + assertEq( + earnings[i] - totalClaimedBefore[i], + tokenBalancesAfter[i] - tokenBalancesBefore[i], + "Token balance not incremented by earnings amount" + ); + } + + cheats.stopPrank(); + } + } + + /// @notice Claim against rootIndex 0 and claim again. Balances should not increment. + /// forge-config: default.fuzz.runs = 10 + function testFuzz_processClaim_Revert_WhenReuseSameClaimAgain( + bool setClaimerFor, + address claimerFor + ) public filterFuzzedAddressInputs(claimerFor) { + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofs(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[0]; + + // 1. Claim against first root + { + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + uint256[] memory totalClaimedBefore = _getCumulativeClaimed(earner, claim); + uint256[] memory earnings = _getCumulativeEarnings(claim); + uint256[] memory tokenBalancesBefore = _getClaimTokenBalances(claimer, claim); + + _assertPaymentClaimedEvents(root, claim, claimer); + paymentCoordinator.processClaim(claim, claimer); + + uint256[] memory tokenBalancesAfter = _getClaimTokenBalances(claimer, claim); + + for (uint256 i = 0; i < totalClaimedBefore.length; ++i) { + assertEq( + earnings[i] - totalClaimedBefore[i], + tokenBalancesAfter[i] - tokenBalancesBefore[i], + "Token balance not incremented by earnings amount" + ); + } + + cheats.stopPrank(); + } + + // 2. Claim against first root again, expect a revert + { + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + cheats.expectRevert( + "PaymentCoordinator.processClaim: cumulativeEarnings must be gt than cumulativeClaimed" + ); + paymentCoordinator.processClaim(claim, claimer); + + cheats.stopPrank(); + } + } + + /// @notice Claim against latest submitted root, rootIndex 3 but modify some of the leaf values + /// forge-config: default.fuzz.runs = 10 + function testFuzz_processClaim_Revert_WhenInvalidTokenClaim( + bool setClaimerFor, + address claimerFor + ) public filterFuzzedAddressInputs(claimerFor) { + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofs(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[2]; + + uint32 rootIndex = claim.rootIndex; + (, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Modify Earnings + claim.tokenLeaves[0].cumulativeEarnings = 1e20; + claim.tokenLeaves[1].cumulativeEarnings = 1e20; + + // Check claim is not valid from both checkClaim() and processClaim() throwing a revert + cheats.startPrank(claimer); + + cheats.expectRevert("PaymentCoordinator._verifyTokenClaim: invalid token claim proof"); + assertFalse(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + cheats.expectRevert("PaymentCoordinator._verifyTokenClaim: invalid token claim proof"); + paymentCoordinator.processClaim(claim, claimer); + + cheats.stopPrank(); + } + + /// @notice Claim against latest submitted root, rootIndex 3 but modify some of the leaf values + /// forge-config: default.fuzz.runs = 10 + function testFuzz_processClaim_Revert_WhenInvalidEarnerClaim( + bool setClaimerFor, + address claimerFor, + address invalidEarner + ) public filterFuzzedAddressInputs(claimerFor) { + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofs(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[2]; + + uint32 rootIndex = claim.rootIndex; + (, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Modify Earner + claim.earnerLeaf.earner = invalidEarner; + + // Check claim is not valid from both checkClaim() and processClaim() throwing a revert + cheats.startPrank(claimer); + + cheats.expectRevert("PaymentCoordinator._verifyEarnerClaimProof: invalid earner claim proof"); + assertFalse(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + cheats.expectRevert("PaymentCoordinator._verifyEarnerClaimProof: invalid earner claim proof"); + paymentCoordinator.processClaim(claim, claimer); + + cheats.stopPrank(); + } + + /// @notice Claim against latest submitted root, rootIndex 3 but write to cumulativeClaimed storage. + /// Expects a revert when calling processClaim() + function testFuzz_processClaim_Revert_WhenCumulativeClaimedUnderflow( + bool setClaimerFor, + address claimerFor + ) public filterFuzzedAddressInputs(claimerFor) { + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofs(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[2]; + + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + // Set cumulativeClaimed to be max uint256, should revert when attempting to claim + stdstore + .target(address(paymentCoordinator)) + .sig("cumulativeClaimed(address,address)") + .with_key(claim.earnerLeaf.earner) + .with_key(address(claim.tokenLeaves[0].token)) + .checked_write(type(uint256).max); + cheats.startPrank(claimer); + cheats.expectRevert("PaymentCoordinator.processClaim: cumulativeEarnings must be gt than cumulativeClaimed"); + paymentCoordinator.processClaim(claim, claimer); + cheats.stopPrank(); + } + + /// @notice Claim against latest submitted root, rootIndex 3 but with larger tokenIndex used that could pass the proofs. + /// Expects a revert as we check for this in processClaim() + function testFuzz_processClaim_Revert_WhenTokenIndexBitwiseAddedTo( + bool setClaimerFor, + address claimerFor, + uint8 numShift + ) public filterFuzzedAddressInputs(claimerFor) { + cheats.assume(0 < numShift && numShift < 16); + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofs(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[2]; + + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + // Take the tokenIndex and add a significant bit so that the actual index number is increased + // but still with the same least significant bits for valid proofs + uint8 proofLength = uint8(claim.tokenTreeProofs[0].length); + claim.tokenIndices[0] = claim.tokenIndices[0] | uint32(1 << (numShift + proofLength / 32)); + cheats.startPrank(claimer); + cheats.expectRevert("PaymentCoordinator._verifyTokenClaim: invalid tokenLeafIndex"); + paymentCoordinator.processClaim(claim, claimer); + cheats.stopPrank(); + } + + /// @notice Claim against latest submitted root, rootIndex 3 but with larger earnerIndex used that could pass the proofs. + /// Expects a revert as we check for this in processClaim() + function testFuzz_processClaim_Revert_WhenEarnerIndexBitwiseAddedTo( + bool setClaimerFor, + address claimerFor, + uint8 numShift + ) public filterFuzzedAddressInputs(claimerFor) { + cheats.assume(0 < numShift && numShift < 16); + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofs(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[2]; + + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + // Take the tokenIndex and add a significant bit so that the actual index number is increased + // but still with the same least significant bits for valid proofs + uint8 proofLength = uint8(claim.earnerTreeProof.length); + claim.earnerIndex = claim.earnerIndex | uint32(1 << (numShift + proofLength / 32)); + cheats.startPrank(claimer); + cheats.expectRevert("PaymentCoordinator._verifyEarnerClaimProof: invalid earnerLeafIndex"); + paymentCoordinator.processClaim(claim, claimer); + cheats.stopPrank(); + } + + /// @notice tests with earnerIndex and tokenIndex set to max value and using alternate claim proofs + function testFuzz_processClaim_WhenMaxEarnerIndexAndTokenIndex( + bool setClaimerFor, + address claimerFor, + uint8 numShift + ) public filterFuzzedAddressInputs(claimerFor) { + // Hardcode earner address to earner in alternate claim proofs + earner = 0x25A1B7322f9796B26a4Bec125913b34C292B28D6; + + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofsMaxEarnerAndLeafIndices(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[0]; + + // 1. Claim against first root where earner tree is full tree and earner and token index is last index of that tree height + { + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + uint256[] memory totalClaimedBefore = _getCumulativeClaimed(earner, claim); + uint256[] memory earnings = _getCumulativeEarnings(claim); + uint256[] memory tokenBalancesBefore = _getClaimTokenBalances(claimer, claim); + + // +1 since earnerIndex is 0-indexed + // Here the earnerIndex is 7 in a full binary tree and the number of bytes32 hash proofs is 3 + assertEq( + claim.earnerIndex + 1, + (1 << ((claim.earnerTreeProof.length / 32))), + "EarnerIndex not set to max value" + ); + // +1 since tokenIndex is 0-indexed + // Here the tokenIndex is also 7 in a full binary tree and the number of bytes32 hash proofs is 3 + assertEq( + claim.tokenIndices[0] + 1, + (1 << ((claim.tokenTreeProofs[0].length / 32))), + "TokenIndex not set to max value" + ); + _assertPaymentClaimedEvents(root, claim, claimer); + paymentCoordinator.processClaim(claim, claimer); + + uint256[] memory tokenBalancesAfter = _getClaimTokenBalances(claimer, claim); + + for (uint256 i = 0; i < totalClaimedBefore.length; ++i) { + assertEq( + earnings[i] - totalClaimedBefore[i], + tokenBalancesAfter[i] - tokenBalancesBefore[i], + "Token balance not incremented by earnings amount" + ); + } + + cheats.stopPrank(); + } + } + + /// @notice tests with single token leaf for the earner's subtree. tokenTreeProof for the token in the claim should be empty + function testFuzz_processClaim_WhenSingleTokenLeaf( + bool setClaimerFor, + address claimerFor, + uint8 numShift + ) public filterFuzzedAddressInputs(claimerFor) { + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofsSingleTokenLeaf(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[0]; + + // 1. Claim against first root where earner tree is full tree and earner and token index is last index of that tree height + { + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + uint256[] memory totalClaimedBefore = _getCumulativeClaimed(earner, claim); + uint256[] memory earnings = _getCumulativeEarnings(claim); + uint256[] memory tokenBalancesBefore = _getClaimTokenBalances(claimer, claim); + + // Single tokenLeaf in earner's subtree, should be 0 index + assertEq( + claim.tokenIndices[0], + 0, + "TokenIndex should be 0" + ); + assertEq( + claim.tokenTreeProofs[0].length, + 0, + "TokenTreeProof should be empty" + ); + _assertPaymentClaimedEvents(root, claim, claimer); + paymentCoordinator.processClaim(claim, claimer); + + uint256[] memory tokenBalancesAfter = _getClaimTokenBalances(claimer, claim); + + for (uint256 i = 0; i < totalClaimedBefore.length; ++i) { + assertEq( + earnings[i] - totalClaimedBefore[i], + tokenBalancesAfter[i] - tokenBalancesBefore[i], + "Token balance not incremented by earnings amount" + ); + } + + cheats.stopPrank(); + } + } + + /// @notice tests with single earner leaf in the merkle tree. earnerTreeProof in claim should be empty + function testFuzz_processClaim_WhenSingleEarnerLeaf( + bool setClaimerFor, + address claimerFor, + uint8 numShift + ) public filterFuzzedAddressInputs(claimerFor) { + // Hardcode earner address to earner in alternate claim proofs + earner = 0x0D6bA28b9919CfCDb6b233469Cc5Ce30b979e08E; + + // if setClaimerFor is true, set the earners claimer to the fuzzed address + address claimer; + if (setClaimerFor) { + cheats.prank(earner); + paymentCoordinator.setClaimerFor(claimerFor); + claimer = claimerFor; + } else { + claimer = earner; + } + + // Parse all 3 claim proofs for distributionRoots 0,1,2 respectively + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = _parseAllProofsSingleEarnerLeaf(); + IPaymentCoordinator.PaymentMerkleClaim memory claim = claims[0]; + + // 1. Claim against first root where earner tree is full tree and earner and token index is last index of that tree height + { + uint32 rootIndex = claim.rootIndex; + (bytes32 root, , uint32 activatedAt) = paymentCoordinator.distributionRoots(rootIndex); + cheats.warp(activatedAt); + + // Claim against root and check balances before/after, and check it matches the difference between + // cumulative claimed and earned. + cheats.startPrank(claimer); + assertTrue(paymentCoordinator.checkClaim(claim), "PaymentCoordinator.checkClaim: claim not valid"); + + uint256[] memory totalClaimedBefore = _getCumulativeClaimed(earner, claim); + uint256[] memory earnings = _getCumulativeEarnings(claim); + uint256[] memory tokenBalancesBefore = _getClaimTokenBalances(claimer, claim); + + // Earner Leaf in merkle tree, should be 0 index + assertEq( + claim.earnerIndex, + 0, + "EarnerIndex should be 0" + ); + assertEq( + claim.earnerTreeProof.length, + 0, + "EarnerTreeProof should be empty" + ); + _assertPaymentClaimedEvents(root, claim, claimer); + paymentCoordinator.processClaim(claim, claimer); + + uint256[] memory tokenBalancesAfter = _getClaimTokenBalances(claimer, claim); + + for (uint256 i = 0; i < totalClaimedBefore.length; ++i) { + assertEq( + earnings[i] - totalClaimedBefore[i], + tokenBalancesAfter[i] - tokenBalancesBefore[i], + "Token balance not incremented by earnings amount" + ); + } + + cheats.stopPrank(); + } + } + + /// @notice Set address with ERC20Mock bytecode and mint amount to paymentCoordinator for + /// balance for testing processClaim() + function _setAddressAsERC20(address randAddress, uint256 mintAmount) internal { + cheats.etch(randAddress, mockTokenBytecode); + ERC20Mock(randAddress).mint(address(paymentCoordinator), mintAmount); + } + + /// @notice parse proofs from json file and submitRoot() + function _parseProofData(string memory filePath) internal returns (IPaymentCoordinator.PaymentMerkleClaim memory) { + cheats.readFile(filePath); + + string memory claimProofData = cheats.readFile(filePath); + + // Parse PaymentMerkleClaim + merkleRoot = abi.decode(stdJson.parseRaw(claimProofData, ".Root"), (bytes32)); + earnerIndex = abi.decode(stdJson.parseRaw(claimProofData, ".EarnerIndex"), (uint32)); + earnerTreeProof = abi.decode(stdJson.parseRaw(claimProofData, ".EarnerTreeProof"), (bytes)); + proofEarner = stdJson.readAddress(claimProofData, ".EarnerLeaf.Earner"); + require(earner == proofEarner, "earner in test and json file do not match"); + earnerTokenRoot = abi.decode(stdJson.parseRaw(claimProofData, ".EarnerLeaf.EarnerTokenRoot"), (bytes32)); + uint256 numTokenLeaves = stdJson.readUint(claimProofData, ".TokenLeavesNum"); + uint256 numTokenTreeProofs = stdJson.readUint(claimProofData, ".TokenTreeProofsNum"); + + IPaymentCoordinator.TokenTreeMerkleLeaf[] memory tokenLeaves = new IPaymentCoordinator.TokenTreeMerkleLeaf[]( + numTokenLeaves + ); + uint32[] memory tokenIndices = new uint32[](numTokenLeaves); + for (uint256 i = 0; i < numTokenLeaves; ++i) { + string memory tokenKey = string.concat(".TokenLeaves[", cheats.toString(i), "].Token"); + string memory amountKey = string.concat(".TokenLeaves[", cheats.toString(i), "].CumulativeEarnings"); + string memory leafIndicesKey = string.concat(".LeafIndices[", cheats.toString(i), "]"); + + IERC20 token = IERC20(stdJson.readAddress(claimProofData, tokenKey)); + uint256 cumulativeEarnings = stdJson.readUint(claimProofData, amountKey); + tokenLeaves[i] = IPaymentCoordinator.TokenTreeMerkleLeaf({ + token: token, + cumulativeEarnings: cumulativeEarnings + }); + tokenIndices[i] = uint32(stdJson.readUint(claimProofData, leafIndicesKey)); + + /// DeployCode ERC20 to Token Address + // deployCodeTo("ERC20PresetFixedSupply.sol", address(tokenLeaves[i].token)); + _setAddressAsERC20(address(token), cumulativeEarnings); + } + bytes[] memory tokenTreeProofs = new bytes[](numTokenTreeProofs); + for (uint256 i = 0; i < numTokenTreeProofs; ++i) { + string memory tokenTreeProofKey = string.concat(".TokenTreeProofs[", cheats.toString(i), "]"); + tokenTreeProofs[i] = abi.decode(stdJson.parseRaw(claimProofData, tokenTreeProofKey), (bytes)); + } + + uint32 rootCalculationEndTimestamp = uint32(block.timestamp); + uint32 activatedAt = uint32(block.timestamp) + paymentCoordinator.activationDelay(); + prevRootCalculationEndTimestamp = rootCalculationEndTimestamp; + cheats.warp(activatedAt); + + uint32 rootIndex = uint32(paymentCoordinator.getDistributionRootsLength()); + + cheats.prank(paymentUpdater); + paymentCoordinator.submitRoot(merkleRoot, prevRootCalculationEndTimestamp); + + IPaymentCoordinator.PaymentMerkleClaim memory newClaim = IPaymentCoordinator.PaymentMerkleClaim({ + rootIndex: rootIndex, + earnerIndex: earnerIndex, + earnerTreeProof: earnerTreeProof, + earnerLeaf: IPaymentCoordinator.EarnerTreeMerkleLeaf({earner: earner, earnerTokenRoot: earnerTokenRoot}), + tokenIndices: tokenIndices, + tokenTreeProofs: tokenTreeProofs, + tokenLeaves: tokenLeaves + }); + + return newClaim; + } + + function _parseAllProofs() internal virtual returns (IPaymentCoordinator.PaymentMerkleClaim[] memory) { + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = new IPaymentCoordinator.PaymentMerkleClaim[](3); + + claims[0] = _parseProofData("src/test/test-data/paymentCoordinator/processClaimProofs_Root1.json"); + claims[1] = _parseProofData("src/test/test-data/paymentCoordinator/processClaimProofs_Root2.json"); + claims[2] = _parseProofData("src/test/test-data/paymentCoordinator/processClaimProofs_Root3.json"); + + return claims; + } + + function _parseAllProofsMaxEarnerAndLeafIndices() internal virtual returns (IPaymentCoordinator.PaymentMerkleClaim[] memory) { + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = new IPaymentCoordinator.PaymentMerkleClaim[](1); + + claims[0] = _parseProofData("src/test/test-data/paymentCoordinator/processClaimProofs_MaxEarnerAndLeafIndices.json"); + + return claims; + } + + function _parseAllProofsSingleTokenLeaf() internal virtual returns (IPaymentCoordinator.PaymentMerkleClaim[] memory) { + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = new IPaymentCoordinator.PaymentMerkleClaim[](1); + + claims[0] = _parseProofData("src/test/test-data/paymentCoordinator/processClaimProofs_SingleTokenLeaf.json"); + + return claims; + } + + function _parseAllProofsSingleEarnerLeaf() internal virtual returns (IPaymentCoordinator.PaymentMerkleClaim[] memory) { + IPaymentCoordinator.PaymentMerkleClaim[] memory claims = new IPaymentCoordinator.PaymentMerkleClaim[](1); + + claims[0] = _parseProofData("src/test/test-data/paymentCoordinator/processClaimProofs_SingleEarnerLeaf.json"); + + return claims; + } +}