Skip to content

Commit

Permalink
Merge pull request #3 from sanbir/master
Browse files Browse the repository at this point in the history
Events etc
  • Loading branch information
sanbir authored Dec 23, 2024
2 parents 2eba651 + f5c7edc commit e3f406a
Show file tree
Hide file tree
Showing 7 changed files with 328 additions and 52 deletions.
89 changes: 89 additions & 0 deletions src/access/P2pOperator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-FileCopyrightText: 2024 P2P Validator <[email protected]>
// 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);
}
}
69 changes: 69 additions & 0 deletions src/access/P2pOperator2Step.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2024 P2P Validator <[email protected]>
// 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);
}
}
26 changes: 23 additions & 3 deletions src/p2pLendingProxy/IP2pLendingProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,28 @@ 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
);

event P2pLendingProxy__CalledAsAnyFunction(
address indexed _lendingProtocolAddress
);
}
67 changes: 59 additions & 8 deletions src/p2pLendingProxy/P2pLendingProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@ 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__ZeroAddressClient();
error P2pLendingProxy__ZeroAddressAsset();
error P2pLendingProxy__ZeroAssetAmount();
error P2pLendingProxy__ZeroSharesAmount();
error P2pLendingProxy__InvalidClientBasisPoints(uint96 _clientBasisPoints);

error P2pLendingProxy__NotFactory(address _factory);

Expand Down Expand Up @@ -90,10 +96,16 @@ 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;

emit P2pLendingProxy__Initialized(_client, _clientBasisPoints);
emit P2pLendingProxy__Initialized();
}

function deposit(
Expand All @@ -106,9 +118,19 @@ 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());

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;

Expand All @@ -125,10 +147,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);
}
Expand All @@ -144,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);

Expand Down Expand Up @@ -187,6 +213,31 @@ 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 callAnyFunction(
address _lendingProtocolAddress,
bytes calldata _lendingProtocolCalldata
)
external
onlyClient
nonReentrant
calldataShouldBeAllowed(_lendingProtocolAddress, _lendingProtocolCalldata, FunctionType.None)
{
emit P2pLendingProxy__CalledAsAnyFunction(_lendingProtocolAddress);
_lendingProtocolAddress.functionCall(_lendingProtocolCalldata);
}

function checkCalldata(
Expand Down
23 changes: 23 additions & 0 deletions src/p2pLendingProxyFactory/IP2pLendingProxyFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit e3f406a

Please sign in to comment.