### Objective
Objective of this review is to figure out if there is any security loophole in the contract and if it adheres to the requirement as is expected
### Requirements
- An owner should be able to distribute token rewards through a merkle distributor
- This should be interfaced via a periphery, such that users need to call only one function instead of calling multiple claims
- The owner should be able to withdraw post deadline in case there is still funds in the distributor
### Actors
- Owner - the address that owns the periphery by extension the owner of the merkle distributors. They can call `create`, `withdraw`, `claim` except the view functions
- Users - the addresses that can claim the amounts in the distributors through the periphery. They can call `claim` execpt the view functions
### Dependencies
- **OpenZeppelin Contracts**:
- `IERC20`: Interface for ERC20 standard.
- `SafeERC20`: Library for safe ERC20 operations.
- `Ownable`: Contract module for single owner authorization.
- `Ownable2Step`: Extension of `Ownable` for two-step ownership transfer.
- `Create2`: Library for deterministic contract deployment.
- `IOwnable2Step`: Extended ownership management interface.
- **Uniswap Interfaces and Contracts**:
- `IMerkleDistributorWithDeadline`: Interface for Merkle Distributors with deadlines.
- `MerkleDistributorWithDeadline`: Implementation of a Merkle Distributor with a deadline.
- Merkle Distributor Periphery Interface
- `IMerkleDistributorPeriphery`: Interface for the Merkle Distributor Periphery contract.
- **Custom Types**
- `Amount`: Type for handling amounts.
- `Id`: Type for handling IDs.
- `Index`: Type for handling indices.
- `MerkleProof`: Type for handling Merkle Proofs.
- `MerkleRoot`: Type for handling Merkle Roots.
- `Number`: Type for handling numerical values.
- `Timestamp`: Type for handling timestamps.
### Contract Structure

1. **State Variables**:
- `storedDistributors`: Array storing `IMerkleDistributorWithDeadline` contract addresses.
2. **Constructor**:
- Initializes the contract, setting the deployer as the owner.
- Documentation correct?
1. **Functions**:
- **View Functions**:
- `merkleDistributor(Id id)`, `totalMerkleDistributors()`, `areClaimed(Query[] calldata queries)`
- **Transaction Functions**:
- `create(IERC20 token, Amount totalAmount, MerkleRoot merkleRoot, Timestamp endTime)`, `claim(Order[] calldata orders)`, `withdraw(IMerkleDistributorWithDeadline[] calldata distributors)`
- **Ownership Functions** (Overridden from `Ownable` and `Ownable2Step`):
- `owner()`, `pendingOwner()`, `transferOwnership(address newOwner)`, `acceptOwnership()`, `renounceOwnership()`
2. **Modifiers**:
- `onlyOwner`: Restricts function access to the contract owner.
- Note: this utilises OZ access controls, and the owner is set in the constructor
1. **Events**:
- `Create`, `Withdraw`
- Note: No events emmited on the periphery for claim
2. **Error Handling**:
- Reverts for invalid token addresses, zero amounts, and end times in the past.
### Specific Checks done
- Constructor mismatch
- Access control
- Overflow/underflow
- Rentrancy
- Unchecked external call
- Loops
- Dependency management
- Logic review
- Functionality
### Test checks
- All tests are passing
- Coverage report as of now
- 
- `test_MerkleProofs` - check if the merkle proof is generated as is expected
- `test_OwnershipOfContract` - check if the owner is set correctly
- `test_TransferabilityOfContract` - check the ownership transfer
- `test_TransferProcessEmitsTheCorrectEvents` - check the ownership events
- `test_CreateFunctionDeploysWithPrecomputeAddress` - check if the precomputed address in create2 is correct
- `test_CreateFunctionDeploysWithCorrectAddress`
- `test_CreateFunctionDeploysWithCorrectId` - check if the id is length of the distributors
- `test_CreateFunctionDeploysTheContractWithCorrectOwnership` - check the ownership of the distributor
- `test_CreateFunctionDeploysTheContractWithCorrectToken` - check the token in the distributor
- `test_CreateFunctionDeploysTheContractWithCorrectMerkleRoot` - check the merkle root
- `test_CreateFunctionDeploysTheContractWithCorrectEndTime` - check the deadline
- `test_CreateFunctionChangesTheTokenBalancesCorrectly` - check if the distributor has the right balance post creation
- `test_CreateFunctionDoesNotLeaveAnyTokensInPeriphery` - check periphery does not hold any tokens post create
- `test_CreateFunctionCallsTheTransferFunctionCorrectly` - check the transfer call made in the periphery
- `test_CreateFunctionEmitsTheCreateEvent` - check if create event is emmited when the create function is called
- `test_CreateFunctionRevertsWhenTokenAddressIsZero` - check if error is emmited it reverts when token address is zero
- `test_CreateFunctionRevertsWhenTotalAmountIsZero` - check if error is emitted when amount is zero
- `test_CreateFunctionRevertsWhenEndTimeIsInThePast` - check if error is emitted when deadline is in the past
- `test_CreateFunctionRevertsWhenCalledByNonOwner` - check if error is emmitted when called by non owner
- `test_AreClaimedFunctionCallsTheIsClaimedFunctionFromDistributors` - check claims are working as expected
- `test_ClaimFunctionCallsTheClaimFunctionFromDistributors` - check if the claim is called on the correct distributor peripheries
- `test_WithdrawFunctionCallsWithdrawFunctionFromDistributors` - check if the withdraw id working as expected
- `test_WithdrawFunctionReturnsTheAmountWithdrawn` - checks if the amount withdrawn is the same as the input
- `test_WithdrawFunctionDoesNotLeaveTokensInPeriphery` - check if withdraw doesnt leave any funds in the periphery
- `test_WithdrawFunctionChangesTokenBalancesCorrectly` - check if withdraw function updates balance correctly
- `test_WithdrawFunctionCallsTheTransferFunctionsCorrectly` - check if the withdraw function calls the correct transfer call
- `test_WithdrawFunctionEmitsTheWithdrawEventsCorrectly` - check the withdraw event emitted
- `test_WithdrawFunctionRevertsWhenCalledByNonOwner` - checks the error emitted when withdraw is called by a non user
- Things that need better tests
- Merkle proof generation is tested with only 1 example
- All the array inputs are fuzzed mostly with length < 100, the test fails to execute when you put larger constraints e.g. 1000
- Though unit tests are exhaustive there are no invariants tested and there is no integration testing
- test_CreateFunctionDeploysWithCorrectAddress - Inconclusive test