From ccd0e93aaec07a5a508dea152adc0855d34aa7d6 Mon Sep 17 00:00:00 2001 From: "alexander.biryukov" Date: Mon, 23 Dec 2024 11:01:36 +0400 Subject: [PATCH 1/5] add P2pOperator2Step --- src/access/P2pOperator.sol | 89 +++++++++++++++++++ src/access/P2pOperator2Step.sol | 69 ++++++++++++++ .../P2pLendingProxyFactory.sol | 32 +------ 3 files changed, 162 insertions(+), 28 deletions(-) create mode 100644 src/access/P2pOperator.sol create mode 100644 src/access/P2pOperator2Step.sol diff --git a/src/access/P2pOperator.sol b/src/access/P2pOperator.sol new file mode 100644 index 0000000..5ae7df7 --- /dev/null +++ b/src/access/P2pOperator.sol @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2024 P2P Validator +// SPDX-License-Identifier: MIT + +// Copy and rename of OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) + +pragma solidity 0.8.27; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an P2pOperator) that can be granted exclusive access to + * specific functions. + * + * The initial P2pOperator is set to the address provided by the deployer. This can + * later be changed with {transferP2pOperator}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyP2pOperator`, which can be applied to your functions to restrict their use to + * the P2pOperator. + */ +abstract contract P2pOperator { + address private s_p2pOperator; + + /** + * @dev The caller account is not authorized to perform an operation. + */ + error P2pOperator__UnauthorizedAccount(address _account); + + /** + * @dev The P2pOperator is not a valid P2pOperator account. (eg. `address(0)`) + */ + error P2pOperator__InvalidP2pOperator(address _p2pOperator); + + event P2pOperator__P2pOperatorTransferred(address indexed _previousP2pOperator, address indexed _newP2pOperator); + + /** + * @dev Initializes the contract setting the address provided by the deployer as the initial P2pOperator. + */ + constructor(address _initialP2pOperator) { + if (_initialP2pOperator == address(0)) { + revert P2pOperator__InvalidP2pOperator(address(0)); + } + _transferP2pOperator(_initialP2pOperator); + } + + /** + * @dev Throws if called by any account other than the P2pOperator. + */ + modifier onlyP2pOperator() { + _checkP2pOperator(); + _; + } + + /** + * @dev Returns the address of the current P2pOperator. + */ + function getP2pOperator() public view virtual returns (address) { + return s_p2pOperator; + } + + /** + * @dev Throws if the sender is not the P2pOperator. + */ + function _checkP2pOperator() internal view virtual { + if (s_p2pOperator != msg.sender) { + revert P2pOperator__UnauthorizedAccount(msg.sender); + } + } + + /** + * @dev Transfers P2pOperator of the contract to a new account (`_newP2pOperator`). + * Can only be called by the current P2pOperator. + */ + function transferP2pOperator(address _newP2pOperator) public virtual onlyP2pOperator { + if (_newP2pOperator == address(0)) { + revert P2pOperator__InvalidP2pOperator(address(0)); + } + _transferP2pOperator(_newP2pOperator); + } + + /** + * @dev Transfers P2pOperator of the contract to a new account (`_newP2pOperator`). + * Internal function without access restriction. + */ + function _transferP2pOperator(address _newP2pOperator) internal virtual { + address oldP2pOperator = s_p2pOperator; + s_p2pOperator = _newP2pOperator; + emit P2pOperator__P2pOperatorTransferred(oldP2pOperator, _newP2pOperator); + } +} diff --git a/src/access/P2pOperator2Step.sol b/src/access/P2pOperator2Step.sol new file mode 100644 index 0000000..d18fe24 --- /dev/null +++ b/src/access/P2pOperator2Step.sol @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2024 P2P Validator +// SPDX-License-Identifier: MIT + +// Copy and rename of OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol) + +pragma solidity 0.8.27; + +import {P2pOperator} from "./P2pOperator.sol"; + +/** + * @dev Contract module which provides access control mechanism, where + * there is an account (a P2pOperator) that can be granted exclusive access to + * specific functions. + * + * This extension of the {P2pOperator.sol} contract includes a two-step mechanism to transfer + * P2pOperator, where the new P2pOperator must call {acceptP2pOperator} in order to replace the + * old one. This can help prevent common mistakes, such as transfers of P2pOperator to + * incorrect accounts, or to contracts that are unable to interact with the + * permission system. + * + * The initial P2pOperator is specified at deployment time in the constructor for `P2pOperator.sol`. This + * can later be changed with {transferP2pOperator} and {acceptP2pOperator}. + * + * This module is used through inheritance. It will make available all functions + * from parent (P2pOperator.sol). + */ +abstract contract P2pOperator2Step is P2pOperator { + address private s_pendingP2pOperator; + + event P2pOperator2Step__P2pOperatorTransferStarted(address indexed _previousP2pOperator, address indexed _newP2pOperator); + + /** + * @dev Returns the address of the pending P2pOperator. + */ + function getPendingP2pOperator() public view virtual returns (address) { + return s_pendingP2pOperator; + } + + /** + * @dev Starts the P2pOperator transfer of the contract to a new account. Replaces the pending transfer if there is one. + * Can only be called by the current P2pOperator. + * + * Setting `_newP2pOperator` to the zero address is allowed; this can be used to cancel an initiated P2pOperator transfer. + */ + function transferP2pOperator(address _newP2pOperator) public virtual override onlyP2pOperator { + s_pendingP2pOperator = _newP2pOperator; + emit P2pOperator2Step__P2pOperatorTransferStarted(getP2pOperator(), _newP2pOperator); + } + + /** + * @dev Transfers P2pOperator of the contract to a new account (`_newP2pOperator`) and deletes any pending P2pOperator. + * Internal function without access restriction. + */ + function _transferP2pOperator(address _newP2pOperator) internal virtual override { + delete s_pendingP2pOperator; + super._transferP2pOperator(_newP2pOperator); + } + + /** + * @dev The new P2pOperator accepts the P2pOperator transfer. + */ + function acceptP2pOperator() public virtual { + address sender = msg.sender; + if (s_pendingP2pOperator != sender) { + revert P2pOperator__UnauthorizedAccount(sender); + } + _transferP2pOperator(sender); + } +} diff --git a/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol b/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol index 96b312d..a74f016 100644 --- a/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol +++ b/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol @@ -9,16 +9,13 @@ import "../@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import "../@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "../@permit2/interfaces/IAllowanceTransfer.sol"; import "../@permit2/libraries/PermitHash.sol"; +import "../access/P2pOperator2Step.sol"; import "../common/AllowedCalldataChecker.sol"; +import "../common/P2pStructs.sol"; import "../p2pLendingProxy/P2pLendingProxy.sol"; import "./IP2pLendingProxyFactory.sol"; -import "../common/P2pStructs.sol"; error P2pLendingProxyFactory__InvalidP2pSignerSignature(); -error P2pLendingProxyFactory__NotP2pOperatorCalled( - address _msgSender, - address _actualP2pOperator -); error P2pLendingProxyFactory__P2pSignerSignatureExpired( uint256 _p2pSignerSigDeadline ); @@ -52,6 +49,7 @@ error P2pLendingProxyFactory__CalldataEndsWithRuleViolated( contract P2pLendingProxyFactory is AllowedCalldataChecker, + P2pOperator2Step, P2pStructs, ERC165, IP2pLendingProxyFactory { @@ -68,16 +66,6 @@ contract P2pLendingProxyFactory is mapping(FunctionType => mapping(address => mapping(bytes4 => Rule[]))) private s_calldataRules; address private s_p2pSigner; - address private s_p2pOperator; - - /// @notice If caller is not P2P operator, revert - modifier onlyP2pOperator() { - address p2pOperator = s_p2pOperator; - if (msg.sender != p2pOperator) { - revert P2pLendingProxyFactory__NotP2pOperatorCalled(msg.sender, p2pOperator); - } - _; - } modifier p2pSignerSignatureShouldNotExpire(uint256 _p2pSignerSigDeadline) { require ( @@ -106,18 +94,10 @@ contract P2pLendingProxyFactory is _; } - constructor(address _p2pSigner, address _p2pTreasury) { + constructor(address _p2pSigner, address _p2pTreasury) P2pOperator(msg.sender) { i_referenceP2pLendingProxy = new P2pLendingProxy(address(this), _p2pTreasury); s_p2pSigner = _p2pSigner; - s_p2pOperator = msg.sender; - } - - // TODO: add 2 step - function setP2pOperator( - address _newP2pOperator - ) external onlyP2pOperator { - s_p2pOperator = _newP2pOperator; } function setP2pSigner( @@ -324,10 +304,6 @@ contract P2pLendingProxyFactory is return s_p2pSigner; } - function getP2pOperator() external view returns (address) { - return s_p2pOperator; - } - /// @notice Calculates the salt required for deterministic clone creation /// depending on client address and client basis points /// @param _clientAddress address From 0e406c30216e9b12d3419b2fd8a67f711ba04042 Mon Sep 17 00:00:00 2001 From: "alexander.biryukov" Date: Mon, 23 Dec 2024 11:54:09 +0400 Subject: [PATCH 2/5] add events --- src/p2pLendingProxy/IP2pLendingProxy.sol | 22 +++++++++-- src/p2pLendingProxy/P2pLendingProxy.sol | 37 +++++++++++++++---- .../IP2pLendingProxyFactory.sol | 23 ++++++++++++ .../P2pLendingProxyFactory.sol | 25 ++++++++++++- 4 files changed, 95 insertions(+), 12 deletions(-) diff --git a/src/p2pLendingProxy/IP2pLendingProxy.sol b/src/p2pLendingProxy/IP2pLendingProxy.sol index 1a05332..b41fbc8 100644 --- a/src/p2pLendingProxy/IP2pLendingProxy.sol +++ b/src/p2pLendingProxy/IP2pLendingProxy.sol @@ -11,8 +11,24 @@ import "../common/IAllowedCalldataChecker.sol"; /// @dev External interface of P2pLendingProxy declared to support ERC165 detection. interface IP2pLendingProxy is IAllowedCalldataChecker, IERC165 { - event P2pLendingProxy__Initialized( - address _client, - uint96 _clientBasisPoints + event P2pLendingProxy__Initialized(); + + event P2pLendingProxy__Deposited( + address indexed _lendingProtocolAddress, + address indexed _asset, + uint160 _amount, + uint256 _totalDepositedAfter + ); + + event P2pLendingProxy__Withdrawn( + address indexed _lendingProtocolAddress, + address indexed _vault, + address indexed _asset, + uint256 _shares, + uint256 _assets, + uint256 _totalWithdrawnAfter, + uint256 _newProfit, + uint256 _p2pAmount, + uint256 _clientAmount ); } \ No newline at end of file diff --git a/src/p2pLendingProxy/P2pLendingProxy.sol b/src/p2pLendingProxy/P2pLendingProxy.sol index 20e2855..84fd4f9 100644 --- a/src/p2pLendingProxy/P2pLendingProxy.sol +++ b/src/p2pLendingProxy/P2pLendingProxy.sol @@ -9,14 +9,14 @@ import "../@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../@openzeppelin/contracts/utils/Address.sol"; import "../@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "../@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; +import {IERC4626} from "../@openzeppelin/contracts/interfaces/IERC4626.sol"; import "../@permit2/interfaces/IAllowanceTransfer.sol"; import "../@permit2/libraries/Permit2Lib.sol"; import "../@permit2/libraries/SignatureVerification.sol"; import "../common/AllowedCalldataChecker.sol"; -import "../p2pLendingProxyFactory/IP2pLendingProxyFactory.sol"; import "../common/P2pStructs.sol"; +import "../p2pLendingProxyFactory/IP2pLendingProxyFactory.sol"; import "./IP2pLendingProxy.sol"; -import {IERC4626} from "../@openzeppelin/contracts/interfaces/IERC4626.sol"; error P2pLendingProxy__NotFactory(address _factory); @@ -93,7 +93,7 @@ contract P2pLendingProxy is s_client = _client; s_clientBasisPoints = _clientBasisPoints; - emit P2pLendingProxy__Initialized(_client, _clientBasisPoints); + emit P2pLendingProxy__Initialized(); } function deposit( @@ -108,7 +108,14 @@ contract P2pLendingProxy is address asset = _permitSingleForP2pLendingProxy.details.token; uint160 amount = _permitSingleForP2pLendingProxy.details.amount; - s_totalDeposited[asset] += amount; + uint256 totalDepositedAfter = s_totalDeposited[asset] + amount; + s_totalDeposited[asset] = totalDepositedAfter; + emit P2pLendingProxy__Deposited( + _lendingProtocolAddress, + asset, + amount, + totalDepositedAfter + ); address client = s_client; @@ -125,10 +132,12 @@ contract P2pLendingProxy is asset ); - IERC20(asset).safeApprove( - address(Permit2Lib.PERMIT2), - type(uint256).max - ); + if (IERC20(asset).allowance(address(this), address(Permit2Lib.PERMIT2)) == 0) { + IERC20(asset).safeApprove( + address(Permit2Lib.PERMIT2), + type(uint256).max + ); + } _lendingProtocolAddress.functionCall(_lendingProtocolCalldata); } @@ -187,6 +196,18 @@ contract P2pLendingProxy is } // clientAmount must be > 0 at this point IERC20(asset).safeTransfer(s_client, clientAmount); + + emit P2pLendingProxy__Withdrawn( + _lendingProtocolAddress, + _vault, + asset, + _shares, + newAssetAmount, + totalWithdrawnAfter, + newProfit, + p2pAmount, + clientAmount + ); } function checkCalldata( diff --git a/src/p2pLendingProxyFactory/IP2pLendingProxyFactory.sol b/src/p2pLendingProxyFactory/IP2pLendingProxyFactory.sol index 75f7b81..656a7db 100644 --- a/src/p2pLendingProxyFactory/IP2pLendingProxyFactory.sol +++ b/src/p2pLendingProxyFactory/IP2pLendingProxyFactory.sol @@ -11,6 +11,29 @@ import "../common/P2pStructs.sol"; /// @dev External interface of P2pLendingProxyFactory interface IP2pLendingProxyFactory is IAllowedCalldataChecker, IERC165 { + event P2pLendingProxyFactory__P2pSignerTransferred( + address indexed _previousP2pSigner, + address indexed _newP2pSigner + ); + + event P2pLendingProxyFactory__CalldataRulesSet( + P2pStructs.FunctionType indexed _functionType, + address indexed _contract, + bytes4 indexed _selector, + P2pStructs.Rule[] _rules + ); + + event P2pLendingProxyFactory__CalldataRulesRemoved( + P2pStructs.FunctionType indexed _functionType, + address indexed _contract, + bytes4 indexed _selector + ); + + event P2pLendingProxyFactory__Deposited( + address indexed _client, + uint96 indexed _clientBasisPoints + ); + function setCalldataRules( P2pStructs.FunctionType _functionType, address _contract, diff --git a/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol b/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol index a74f016..951585d 100644 --- a/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol +++ b/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol @@ -67,6 +67,8 @@ contract P2pLendingProxyFactory is address private s_p2pSigner; + address[] private s_allProxies; + modifier p2pSignerSignatureShouldNotExpire(uint256 _p2pSignerSigDeadline) { require ( block.timestamp < _p2pSignerSigDeadline, @@ -98,11 +100,13 @@ contract P2pLendingProxyFactory is i_referenceP2pLendingProxy = new P2pLendingProxy(address(this), _p2pTreasury); s_p2pSigner = _p2pSigner; + emit P2pLendingProxyFactory__P2pSignerTransferred(address(0), _p2pSigner); } - function setP2pSigner( + function transferP2pSigner( address _newP2pSigner ) external onlyP2pOperator { + emit P2pLendingProxyFactory__P2pSignerTransferred(s_p2pSigner, _newP2pSigner); s_p2pSigner = _newP2pSigner; } @@ -113,6 +117,12 @@ contract P2pLendingProxyFactory is Rule[] calldata _rules ) external onlyP2pOperator { s_calldataRules[_functionType][_contract][_selector] = _rules; + emit P2pLendingProxyFactory__CalldataRulesSet( + _functionType, + _contract, + _selector, + _rules + ); } function removeCalldataRules( @@ -121,6 +131,11 @@ contract P2pLendingProxyFactory is bytes4 _selector ) external onlyP2pOperator { delete s_calldataRules[_functionType][_contract][_selector]; + emit P2pLendingProxyFactory__CalldataRulesRemoved( + _functionType, + _contract, + _selector + ); } function deposit( @@ -151,6 +166,8 @@ contract P2pLendingProxyFactory is _permit2SignatureForP2pLendingProxy ); + emit P2pLendingProxyFactory__Deposited(msg.sender, _clientBasisPoints); + p2pLendingProxyAddress = address(p2pLendingProxy); } @@ -245,6 +262,8 @@ contract P2pLendingProxyFactory is msg.sender, _clientBasisPoints ); + + s_allProxies.push(address(p2pLendingProxy)); } /// @notice Predicts the address of a P2pLendingProxy contract instance @@ -304,6 +323,10 @@ contract P2pLendingProxyFactory is return s_p2pSigner; } + function getAllProxies() external view returns (address[] memory) { + return s_allProxies; + } + /// @notice Calculates the salt required for deterministic clone creation /// depending on client address and client basis points /// @param _clientAddress address From e3b1af09385bce190644722759a0da9609633d67 Mon Sep 17 00:00:00 2001 From: "alexander.biryukov" Date: Mon, 23 Dec 2024 12:02:26 +0400 Subject: [PATCH 3/5] callAnyFunction --- src/p2pLendingProxy/IP2pLendingProxy.sol | 4 ++++ src/p2pLendingProxy/P2pLendingProxy.sol | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/p2pLendingProxy/IP2pLendingProxy.sol b/src/p2pLendingProxy/IP2pLendingProxy.sol index b41fbc8..4963673 100644 --- a/src/p2pLendingProxy/IP2pLendingProxy.sol +++ b/src/p2pLendingProxy/IP2pLendingProxy.sol @@ -31,4 +31,8 @@ interface IP2pLendingProxy is IAllowedCalldataChecker, IERC165 { uint256 _p2pAmount, uint256 _clientAmount ); + + event P2pLendingProxy__CalledAsAnyFunction( + address indexed _lendingProtocolAddress + ); } \ No newline at end of file diff --git a/src/p2pLendingProxy/P2pLendingProxy.sol b/src/p2pLendingProxy/P2pLendingProxy.sol index 84fd4f9..86bba2a 100644 --- a/src/p2pLendingProxy/P2pLendingProxy.sol +++ b/src/p2pLendingProxy/P2pLendingProxy.sol @@ -210,6 +210,19 @@ contract P2pLendingProxy is ); } + function callAnyFunction( + address _lendingProtocolAddress, + bytes calldata _lendingProtocolCalldata + ) + external + onlyClient + nonReentrant + calldataShouldBeAllowed(_lendingProtocolAddress, _lendingProtocolCalldata, FunctionType.None) + { + emit P2pLendingProxy__CalledAsAnyFunction(_lendingProtocolAddress); + _lendingProtocolAddress.functionCall(_lendingProtocolCalldata); + } + function checkCalldata( address _target, bytes4 _selector, From 2052f63bad8c9892f47317c093f0e7bffc4a604d Mon Sep 17 00:00:00 2001 From: "alexander.biryukov" Date: Mon, 23 Dec 2024 12:48:31 +0400 Subject: [PATCH 4/5] input validation --- src/p2pLendingProxy/P2pLendingProxy.sol | 17 +++++++++++++++++ .../P2pLendingProxyFactory.sol | 15 +++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/p2pLendingProxy/P2pLendingProxy.sol b/src/p2pLendingProxy/P2pLendingProxy.sol index 86bba2a..346a76b 100644 --- a/src/p2pLendingProxy/P2pLendingProxy.sol +++ b/src/p2pLendingProxy/P2pLendingProxy.sol @@ -18,6 +18,12 @@ import "../common/P2pStructs.sol"; import "../p2pLendingProxyFactory/IP2pLendingProxyFactory.sol"; import "./IP2pLendingProxy.sol"; +error P2pLendingProxy__ZeroAddressClient(); +error P2pLendingProxy__ZeroAddressAsset(); +error P2pLendingProxy__ZeroAssetAmount(); +error P2pLendingProxy__ZeroSharesAmount(); +error P2pLendingProxy__InvalidClientBasisPoints(uint96 _clientBasisPoints); + error P2pLendingProxy__NotFactory(address _factory); /// @notice Called by an address other than factory @@ -90,6 +96,12 @@ contract P2pLendingProxy is external onlyFactory { + require(_client != address(0), P2pLendingProxy__ZeroAddressClient()); + require( + _clientBasisPoints > 0 && _clientBasisPoints <= 10_000, + P2pLendingProxy__InvalidClientBasisPoints(_clientBasisPoints) + ); + s_client = _client; s_clientBasisPoints = _clientBasisPoints; @@ -106,7 +118,10 @@ contract P2pLendingProxy is onlyFactory { address asset = _permitSingleForP2pLendingProxy.details.token; + require (asset != address(0), P2pLendingProxy__ZeroAddressAsset()); + uint160 amount = _permitSingleForP2pLendingProxy.details.amount; + require (amount > 0, P2pLendingProxy__ZeroAssetAmount()); uint256 totalDepositedAfter = s_totalDeposited[asset] + amount; s_totalDeposited[asset] = totalDepositedAfter; @@ -153,6 +168,8 @@ contract P2pLendingProxy is nonReentrant calldataShouldBeAllowed(_lendingProtocolAddress, _lendingProtocolCalldata, FunctionType.Withdrawal) { + require (_shares > 0, P2pLendingProxy__ZeroSharesAmount()); + // approve shares from Proxy to Protocol IERC20(_vault).safeApprove(_lendingProtocolAddress, _shares); diff --git a/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol b/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol index 951585d..040ec60 100644 --- a/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol +++ b/src/p2pLendingProxyFactory/P2pLendingProxyFactory.sol @@ -15,6 +15,7 @@ import "../common/P2pStructs.sol"; import "../p2pLendingProxy/P2pLendingProxy.sol"; import "./IP2pLendingProxyFactory.sol"; +error P2pLendingProxyFactory__ZeroP2pSignerAddress(); error P2pLendingProxyFactory__InvalidP2pSignerSignature(); error P2pLendingProxyFactory__P2pSignerSignatureExpired( uint256 _p2pSignerSigDeadline @@ -99,15 +100,13 @@ contract P2pLendingProxyFactory is constructor(address _p2pSigner, address _p2pTreasury) P2pOperator(msg.sender) { i_referenceP2pLendingProxy = new P2pLendingProxy(address(this), _p2pTreasury); - s_p2pSigner = _p2pSigner; - emit P2pLendingProxyFactory__P2pSignerTransferred(address(0), _p2pSigner); + _transferP2pSigner(_p2pSigner); } function transferP2pSigner( address _newP2pSigner ) external onlyP2pOperator { - emit P2pLendingProxyFactory__P2pSignerTransferred(s_p2pSigner, _newP2pSigner); - s_p2pSigner = _newP2pSigner; + _transferP2pSigner(_newP2pSigner); } function setCalldataRules( @@ -234,6 +233,14 @@ contract P2pLendingProxyFactory is } } + function _transferP2pSigner( + address _newP2pSigner + ) private { + require (_newP2pSigner != address(0), P2pLendingProxyFactory__ZeroP2pSignerAddress()); + emit P2pLendingProxyFactory__P2pSignerTransferred(s_p2pSigner, _newP2pSigner); + s_p2pSigner = _newP2pSigner; + } + /// @notice Creates a new P2pLendingProxy contract instance if not created yet function _getOrCreateP2pLendingProxy(uint96 _clientBasisPoints) private From f5c7edc4a7f0d818d3e5728c8cdba218217cd84a Mon Sep 17 00:00:00 2001 From: "alexander.biryukov" Date: Mon, 23 Dec 2024 13:17:13 +0400 Subject: [PATCH 5/5] test USDT --- test/MainnetIntegration.sol | 44 ++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/test/MainnetIntegration.sol b/test/MainnetIntegration.sol index eb4c900..decd422 100644 --- a/test/MainnetIntegration.sol +++ b/test/MainnetIntegration.sol @@ -15,6 +15,8 @@ import {PermitHash} from "../src/@permit2/libraries/PermitHash.sol"; contract MainnetIntegration is Test { + using SafeERC20 for IERC20; + address constant P2pTreasury = 0x6Bb8b45a1C6eA816B70d76f83f7dC4f0f87365Ff; P2pLendingProxyFactory private factory; @@ -30,7 +32,11 @@ contract MainnetIntegration is Test { address constant MorphoEthereumBundlerV2 = 0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077; address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address constant VaultUSDC = 0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458; - // address constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address constant VaultUSDT = 0xbEef047a543E45807105E51A8BBEFCc5950fcfBa; + + address asset; + address vault; uint256 constant SigDeadline = 1734464723; uint96 constant ClientBasisPoints = 8700; // 13% fee @@ -46,8 +52,6 @@ contract MainnetIntegration is Test { p2pOperatorAddress = makeAddr("p2pOperator"); nobody = makeAddr("nobody"); - deal(USDC, clientAddress, 10000e18); - vm.startPrank(p2pOperatorAddress); factory = new P2pLendingProxyFactory(p2pSignerAddress, P2pTreasury); vm.stopPrank(); @@ -55,7 +59,21 @@ contract MainnetIntegration is Test { proxyAddress = factory.predictP2pLendingProxyAddress(clientAddress, ClientBasisPoints); } - function test_HappyPath_Mainnet() external { + function test_HappyPath_USDT_Mainnet() external { + asset = USDT; + vault = VaultUSDT; + _happyPath_Mainnet(); + } + + function test_HappyPath_USDC_Mainnet() external { + asset = USDC; + vault = VaultUSDC; + _happyPath_Mainnet(); + } + + function _happyPath_Mainnet() private { + deal(asset, clientAddress, 10000e18); + // allowed calldata for factory bytes4 multicallSelector = IMorphoEthereumBundlerV2.multicall.selector; @@ -114,7 +132,7 @@ contract MainnetIntegration is Test { // morpho approve2 IAllowanceTransfer.PermitDetails memory permitDetails = IAllowanceTransfer.PermitDetails({ - token: USDC, + token: asset, amount: uint160(DepositAmount), expiration: type(uint48).max, nonce: 0 @@ -135,14 +153,14 @@ contract MainnetIntegration is Test { // morpho transferFrom2 bytes memory transferFrom2CallData = abi.encodeCall(IMorphoEthereumBundlerV2.transferFrom2, ( - USDC, + asset, DepositAmount )); // morpho erc4626Deposit - uint256 shares = IERC4626(VaultUSDC).convertToShares(DepositAmount); + uint256 shares = IERC4626(vault).convertToShares(DepositAmount); bytes memory erc4626Deposit2CallData = abi.encodeCall(IMorphoEthereumBundlerV2.erc4626Deposit, ( - VaultUSDC, + vault, DepositAmount, (shares * 100) / 102, proxyAddress @@ -176,7 +194,7 @@ contract MainnetIntegration is Test { bytes memory p2pSignerSignature = abi.encodePacked(r2, s2, v2); vm.startPrank(clientAddress); - IERC20(USDC).approve(address(Permit2Lib.PERMIT2), type(uint256).max); + IERC20(asset).safeApprove(address(Permit2Lib.PERMIT2), type(uint256).max); factory.deposit( MorphoEthereumBundlerV2, multicallCallData, @@ -189,12 +207,12 @@ contract MainnetIntegration is Test { ); vm.stopPrank(); - uint256 sharesBalance = IERC20(VaultUSDC).balanceOf(proxyAddress); + uint256 sharesBalance = IERC20(vault).balanceOf(proxyAddress); // morpho erc4626Redeem - uint256 assets = IERC4626(VaultUSDC).convertToAssets(sharesBalance); + uint256 assets = IERC4626(vault).convertToAssets(sharesBalance); bytes memory erc4626RedeemCallData = abi.encodeCall(IMorphoEthereumBundlerV2.erc4626Redeem, ( - VaultUSDC, + vault, sharesBalance, (assets * 100) / 102, proxyAddress, @@ -210,7 +228,7 @@ contract MainnetIntegration is Test { P2pLendingProxy(proxyAddress).withdraw( MorphoEthereumBundlerV2, multicallWithdrawalCallData, - VaultUSDC, + vault, sharesBalance ); vm.stopPrank();