Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drops #6

Merged
merged 7 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/common/AllowedCalldataChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ pragma solidity 0.8.27;
import "./IAllowedCalldataChecker.sol";
import "./P2pStructs.sol";

/// @dev Error for when the calldata is too short
error AllowedCalldataChecker__DataTooShort();

/// @title AllowedCalldataChecker
/// @author P2P Validator <[email protected]>
/// @notice Abstract contract for checking if a calldata is allowed
abstract contract AllowedCalldataChecker is IAllowedCalldataChecker {

/// @dev Modifier for checking if a calldata is allowed
/// @param _lendingProtocolAddress The address of the lending protocol
/// @param _lendingProtocolCalldata The calldata (encoded signature + arguments) to be passed to the lending protocol
/// @param _functionType Deposit, Withdraw, or None
modifier calldataShouldBeAllowed(
address _lendingProtocolAddress,
bytes calldata _lendingProtocolCalldata,
Expand All @@ -36,6 +44,11 @@ abstract contract AllowedCalldataChecker is IAllowedCalldataChecker {
return bytes4(_data[:4]);
}

/// @notice Checks if the calldata is allowed
/// @param _target The address of the lending protocol
/// @param _selector The selector of the function
/// @param _calldataAfterSelector The calldata after the selector
/// @param _functionType Deposit, Withdraw, or None
function checkCalldata(
address _target,
bytes4 _selector,
Expand Down
3 changes: 3 additions & 0 deletions src/common/IAllowedCalldataChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ pragma solidity 0.8.27;

import "./P2pStructs.sol";

/// @title IAllowedCalldataChecker
/// @author P2P Validator <[email protected]>
/// @notice Interface for checking if a calldata is allowed
interface IAllowedCalldataChecker {
function checkCalldata(
address _target,
Expand Down
2 changes: 2 additions & 0 deletions src/common/IMorphoBundler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pragma solidity 0.8.27;

import "../@permit2/interfaces/IAllowanceTransfer.sol";

/// @title IMorphoBundler
/// @notice Based on https://github.com/morpho-org/morpho-blue-bundlers
interface IMorphoBundler {
/// @notice Approves the given `amount` of `asset` from the initiator to be spent by `permitSingle.spender` via
/// Permit2 with the given `deadline` & EIP-712 `signature`.
Expand Down
3 changes: 3 additions & 0 deletions src/common/P2pStructs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ abstract contract P2pStructs {

/// @title Enum
enum FunctionType {
/// @notice Any other function type (not deposit or withdrawal)
None,
/// @notice Deposit function type
Deposit,
/// @notice Withdrawal function type
Withdrawal
}

Expand Down
30 changes: 30 additions & 0 deletions src/mocks/@murky/Merkle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import "./common/MurkyBase.sol";

/// @notice Nascent, simple, kinda efficient (and improving!) Merkle proof generator and verifier
/// @author dmfxyz
/// @dev Note Generic Merkle Tree
contract Merkle is MurkyBase {

/********************
* HASHING FUNCTION *
********************/

/// ascending sort and concat prior to hashing
function hashLeafPairs(bytes32 left, bytes32 right) public pure override returns (bytes32 _hash) {
assembly {
switch lt(left, right)
case 0 {
mstore(0x0, right)
mstore(0x20, left)
}
default {
mstore(0x0, left)
mstore(0x20, right)
}
_hash := keccak256(0x0, 0x40)
}
}
}
189 changes: 189 additions & 0 deletions src/mocks/@murky/common/MurkyBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

abstract contract MurkyBase {
/***************
* CONSTRUCTOR *
***************/
constructor() {}

/********************
* VIRTUAL HASHING FUNCTIONS *
********************/
function hashLeafPairs(bytes32 left, bytes32 right) public pure virtual returns (bytes32 _hash);


/**********************
* PROOF VERIFICATION *
**********************/

function verifyProof(bytes32 root, bytes32[] memory proof, bytes32 valueToProve) external pure returns (bool) {
// proof length must be less than max array size
bytes32 rollingHash = valueToProve;
uint256 length = proof.length;
unchecked {
for(uint i = 0; i < length; ++i){
rollingHash = hashLeafPairs(rollingHash, proof[i]);
}
}
return root == rollingHash;
}

/********************
* PROOF GENERATION *
********************/

function getRoot(bytes32[] memory data) public pure returns (bytes32) {
require(data.length > 1, "won't generate root for single leaf");
while(data.length > 1) {
data = hashLevel(data);
}
return data[0];
}

function getProof(bytes32[] memory data, uint256 node) public pure returns (bytes32[] memory) {
require(data.length > 1, "won't generate proof for single leaf");
// The size of the proof is equal to the ceiling of log2(numLeaves)
bytes32[] memory result = new bytes32[](log2ceilBitMagic(data.length));
uint256 pos = 0;

// Two overflow risks: node, pos
// node: max array size is 2**256-1. Largest index in the array will be 1 less than that. Also,
// for dynamic arrays, size is limited to 2**64-1
// pos: pos is bounded by log2(data.length), which should be less than type(uint256).max
while(data.length > 1) {
unchecked {
if(node & 0x1 == 1) {
result[pos] = data[node - 1];
}
else if (node + 1 == data.length) {
result[pos] = bytes32(0);
}
else {
result[pos] = data[node + 1];
}
++pos;
node /= 2;
}
data = hashLevel(data);
}
return result;
}

///@dev function is private to prevent unsafe data from being passed
function hashLevel(bytes32[] memory data) private pure returns (bytes32[] memory) {
bytes32[] memory result;

// Function is private, and all internal callers check that data.length >=2.
// Underflow is not possible as lowest possible value for data/result index is 1
// overflow should be safe as length is / 2 always.
unchecked {
uint256 length = data.length;
if (length & 0x1 == 1){
result = new bytes32[](length / 2 + 1);
result[result.length - 1] = hashLeafPairs(data[length - 1], bytes32(0));
} else {
result = new bytes32[](length / 2);
}
// pos is upper bounded by data.length / 2, so safe even if array is at max size
uint256 pos = 0;
for (uint256 i = 0; i < length-1; i+=2){
result[pos] = hashLeafPairs(data[i], data[i+1]);
++pos;
}
}
return result;
}

/******************
* MATH "LIBRARY" *
******************/

/// @dev Note that x is assumed > 0
function log2ceil(uint256 x) public pure returns (uint256) {
uint256 ceil = 0;
uint pOf2;
// If x is a power of 2, then this function will return a ceiling
// that is 1 greater than the actual ceiling. So we need to check if
// x is a power of 2, and subtract one from ceil if so.
assembly {
// we check by seeing if x == (~x + 1) & x. This applies a mask
// to find the lowest set bit of x and then checks it for equality
// with x. If they are equal, then x is a power of 2.

/* Example
x has single bit set
x := 0000_1000
(~x + 1) = (1111_0111) + 1 = 1111_1000
(1111_1000 & 0000_1000) = 0000_1000 == x

x has multiple bits set
x := 1001_0010
(~x + 1) = (0110_1101 + 1) = 0110_1110
(0110_1110 & x) = 0000_0010 != x
*/

// we do some assembly magic to treat the bool as an integer later on
pOf2 := eq(and(add(not(x), 1), x), x)
}

// if x == type(uint256).max, than ceil is capped at 256
// if x == 0, then pO2 == 0, so ceil won't underflow
unchecked {
while( x > 0) {
x >>= 1;
ceil++;
}
ceil -= pOf2; // see above
}
return ceil;
}

/// Original bitmagic adapted from https://github.com/paulrberg/prb-math/blob/main/contracts/PRBMath.sol
/// @dev Note that x assumed > 1
function log2ceilBitMagic(uint256 x) public pure returns (uint256){
if (x <= 1) {
return 0;
}
uint256 msb = 0;
uint256 _x = x;
if (x >= 2**128) {
x >>= 128;
msb += 128;
}
if (x >= 2**64) {
x >>= 64;
msb += 64;
}
if (x >= 2**32) {
x >>= 32;
msb += 32;
}
if (x >= 2**16) {
x >>= 16;
msb += 16;
}
if (x >= 2**8) {
x >>= 8;
msb += 8;
}
if (x >= 2**4) {
x >>= 4;
msb += 4;
}
if (x >= 2**2) {
x >>= 2;
msb += 2;
}
if (x >= 2**1) {
msb += 1;
}

uint256 lsb = (~_x + 1) & _x;
if ((lsb == _x) && (msb > 0)) {
return msb;
} else {
return msb + 1;
}
}
}
53 changes: 53 additions & 0 deletions src/mocks/IUniversalRewardsDistributor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.27;

/// @notice The pending root struct for a merkle tree distribution during the timelock.
struct PendingRoot {
/// @dev The submitted pending root.
bytes32 root;
/// @dev The optional ipfs hash containing metadata about the root (e.g. the merkle tree itself).
bytes32 ipfsHash;
/// @dev The timestamp at which the pending root can be accepted.
uint256 validAt;
}

/// @dev This interface is used for factorizing IUniversalRewardsDistributorStaticTyping and
/// IUniversalRewardsDistributor.
/// @dev Consider using the IUniversalRewardsDistributor interface instead of this one.
interface IUniversalRewardsDistributorBase {
function root() external view returns (bytes32);
function owner() external view returns (address);
function timelock() external view returns (uint256);
function ipfsHash() external view returns (bytes32);
function isUpdater(address) external view returns (bool);
function claimed(address, address) external view returns (uint256);

function acceptRoot() external;
function setRoot(bytes32 newRoot, bytes32 newIpfsHash) external;
function setTimelock(uint256 newTimelock) external;
function setRootUpdater(address updater, bool active) external;
function revokePendingRoot() external;
function setOwner(address newOwner) external;

function submitRoot(bytes32 newRoot, bytes32 ipfsHash) external;

function claim(address account, address reward, uint256 claimable, bytes32[] memory proof)
external
returns (uint256 amount);
}

/// @dev This interface is inherited by the UniversalRewardsDistributor so that function signatures are checked by the
/// compiler.
/// @dev Consider using the IUniversalRewardsDistributor interface instead of this one.
interface IUniversalRewardsDistributorStaticTyping is IUniversalRewardsDistributorBase {
function pendingRoot() external view returns (bytes32 root, bytes32 ipfsHash, uint256 validAt);
}

/// @title IUniversalRewardsDistributor
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for UniversalRewardsDistributor to have access to all the functions with the appropriate
/// function signatures.
interface IUniversalRewardsDistributor is IUniversalRewardsDistributorBase {
function pendingRoot() external view returns (PendingRoot memory);
}
Loading
Loading