P2pResolvProxyFactory

Description:

Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/adapters/resolv/p2pResolvProxyFactory/P2pResolvProxyFactory.sol": {
      "content": "// SPDX-FileCopyrightText: 2025 P2P Validator <info@p2p.org>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;

import "../../../p2pYieldProxyFactory/P2pYieldProxyFactory.sol";
import "../p2pResolvProxy/P2pResolvProxy.sol";

/// @title Entry point for depositing into Resolv with P2P.org
contract P2pResolvProxyFactory is P2pYieldProxyFactory {

    /// @notice Constructor for P2pResolvProxyFactory
    /// @param _p2pSigner The P2pSigner address
    /// @param _p2pTreasury The P2pTreasury address
    /// @param _stUSR stUSR address
    /// @param _USR USR address
    /// @param _stRESOLV stRESOLV
    /// @param _RESOLV RESOLV
    /// @param _allowedCalldataChecker AllowedCalldataChecker
    /// @param _stakedTokenDistributor StakedTokenDistributor
    constructor(
        address _p2pSigner,
        address _p2pTreasury,
        address _stUSR,
        address _USR,
        address _stRESOLV,
        address _RESOLV,
        address _allowedCalldataChecker,
        address _stakedTokenDistributor
    ) P2pYieldProxyFactory(_p2pSigner) {
        i_referenceP2pYieldProxy = new P2pResolvProxy(
            address(this),
            _p2pTreasury,
            _allowedCalldataChecker,
            _stUSR,
            _USR,
            _stRESOLV,
            _RESOLV,
            _stakedTokenDistributor
        );
    }
}
"
    },
    "src/p2pYieldProxyFactory/P2pYieldProxyFactory.sol": {
      "content": "// SPDX-FileCopyrightText: 2025 P2P Validator <info@p2p.org>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;

import "../@openzeppelin/contracts/proxy/Clones.sol";
import "../@openzeppelin/contracts/utils/Address.sol";
import "../@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import "../@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "../access/P2pOperator2Step.sol";
import "../common/AllowedCalldataChecker.sol";
import "../p2pYieldProxy/P2pYieldProxy.sol";
import "./IP2pYieldProxyFactory.sol";

/// @dev Error when the P2pSigner address is zero
error P2pYieldProxyFactory__ZeroP2pSignerAddress();

/// @dev Error when the P2pSigner signature is invalid
error P2pYieldProxyFactory__InvalidP2pSignerSignature();

/// @dev Error when the P2pSigner signature is expired
error P2pYieldProxyFactory__P2pSignerSignatureExpired(
    uint256 _p2pSignerSigDeadline
);

/// @dev Error when no rules are defined
error P2pYieldProxyFactory__NoRulesDefined(
    address _target,
    bytes4 _selector
);

/// @dev Error when no calldata is allowed
error P2pYieldProxyFactory__NoCalldataAllowed(
    address _target,
    bytes4 _selector
);

/// @dev Error when the calldata is too short for the start with rule
error P2pYieldProxyFactory__CalldataTooShortForStartsWithRule(
    uint256 _calldataAfterSelectorLength,
    uint32 _ruleIndex,
    uint32 _bytesCount
);

/// @dev Error when the calldata starts with rule is violated
error P2pYieldProxyFactory__CalldataStartsWithRuleViolated(
    bytes _actual,
    bytes _expected
);

/// @dev Error when the calldata is too short for the ends with rule
error P2pYieldProxyFactory__CalldataTooShortForEndsWithRule(
    uint256 _calldataAfterSelectorLength,
    uint32 _bytesCount
);

/// @dev Error when the calldata ends with rule is violated
error P2pYieldProxyFactory__CalldataEndsWithRuleViolated(
    bytes _actual,
    bytes _expected
);

/// @title P2pYieldProxyFactory
/// @author P2P Validator <info@p2p.org>
/// @notice P2pYieldProxyFactory is a factory contract for creating P2pYieldProxy contracts
abstract contract P2pYieldProxyFactory is
    AllowedCalldataChecker,
    P2pOperator2Step,
    ERC165,
    IP2pYieldProxyFactory {

    using SignatureChecker for address;
    using ECDSA for bytes32;

    /// @notice Reference P2pYieldProxy contract
    P2pYieldProxy internal immutable i_referenceP2pYieldProxy;

    /// @notice P2pSigner address   
    address internal s_p2pSigner;

    /// @notice All proxies
    address[] internal s_allProxies;

    /// @notice Modifier to check if the P2pSigner signature should not expire
    modifier p2pSignerSignatureShouldNotExpire(uint256 _p2pSignerSigDeadline) {
        require (
            block.timestamp < _p2pSignerSigDeadline,
            P2pYieldProxyFactory__P2pSignerSignatureExpired(_p2pSignerSigDeadline)
        );
        _;
    }

    /// @notice Modifier to check if the P2pSigner signature should be valid
    modifier p2pSignerSignatureShouldBeValid(
        uint96 _clientBasisPoints,
        uint256 _p2pSignerSigDeadline,
        bytes calldata _p2pSignerSignature
    ) {
        require (
            s_p2pSigner.isValidSignatureNow(
            getHashForP2pSigner(
            msg.sender,
            _clientBasisPoints,
            _p2pSignerSigDeadline
                ).toEthSignedMessageHash(),
        _p2pSignerSignature
            ),
            P2pYieldProxyFactory__InvalidP2pSignerSignature()
        );
        _;
    }

    /// @notice Constructor for P2pYieldProxyFactory
    /// @param _p2pSigner The P2pSigner address
    constructor(
        address _p2pSigner
    ) P2pOperator(msg.sender) {
        _transferP2pSigner(_p2pSigner);
    }

    /// @inheritdoc IP2pYieldProxyFactory
    function transferP2pSigner(
        address _newP2pSigner
    ) external onlyP2pOperator {
        _transferP2pSigner(_newP2pSigner);
    }

    /// @inheritdoc IP2pYieldProxyFactory
    function deposit(
        address _asset,
        uint256 _amount,

        uint96 _clientBasisPoints,
        uint256 _p2pSignerSigDeadline,
        bytes calldata _p2pSignerSignature
    )
    external
    p2pSignerSignatureShouldNotExpire(_p2pSignerSigDeadline)
    p2pSignerSignatureShouldBeValid(_clientBasisPoints, _p2pSignerSigDeadline, _p2pSignerSignature)
    returns (address p2pYieldProxyAddress)
    {
        // create proxy if not created yet
        P2pYieldProxy p2pYieldProxy = _getOrCreateP2pYieldProxy(_clientBasisPoints);

        // deposit via proxy
        p2pYieldProxy.deposit(_asset, _amount);

        emit P2pYieldProxyFactory__Deposited(msg.sender, _clientBasisPoints);

        p2pYieldProxyAddress = address(p2pYieldProxy);
    }

    function _transferP2pSigner(
        address _newP2pSigner
    ) private {
        require (_newP2pSigner != address(0), P2pYieldProxyFactory__ZeroP2pSignerAddress());
        emit P2pYieldProxyFactory__P2pSignerTransferred(s_p2pSigner, _newP2pSigner);
        s_p2pSigner = _newP2pSigner;
    }

    /// @notice Creates a new P2pYieldProxy contract instance if not created yet
    function _getOrCreateP2pYieldProxy(uint96 _clientBasisPoints)
    private
    returns (P2pYieldProxy p2pYieldProxy)
    {
        address p2pYieldProxyAddress = predictP2pYieldProxyAddress(
            msg.sender,
            _clientBasisPoints
        );
        uint256 codeSize = p2pYieldProxyAddress.code.length;
        if (codeSize > 0) {
            return P2pYieldProxy(p2pYieldProxyAddress);
        }

        p2pYieldProxy = P2pYieldProxy(
                Clones.cloneDeterministic(
                address(i_referenceP2pYieldProxy),
                _getSalt(
                    msg.sender,
                    _clientBasisPoints
                )
            )
        );

        p2pYieldProxy.initialize(
            msg.sender,
            _clientBasisPoints
        );

        s_allProxies.push(address(p2pYieldProxy));

        emit P2pYieldProxyFactory__ProxyCreated(
            address(p2pYieldProxy),
            msg.sender,
            _clientBasisPoints
        );
    }

    /// @notice Calculates the salt required for deterministic clone creation
    /// depending on client address and client basis points
    /// @param _clientAddress address
    /// @param _clientBasisPoints basis points (10000 = 100%)
    /// @return bytes32 salt
    function _getSalt(
        address _clientAddress,
        uint96 _clientBasisPoints
    ) private pure returns (bytes32)
    {
        return keccak256(abi.encode(_clientAddress, _clientBasisPoints));
    }

    /// @inheritdoc IP2pYieldProxyFactory
    function predictP2pYieldProxyAddress(
        address _client,
        uint96 _clientBasisPoints
    ) public view returns (address) {
        return Clones.predictDeterministicAddress(
            address(i_referenceP2pYieldProxy),
            _getSalt(_client, _clientBasisPoints)
        );
    }

    /// @inheritdoc IP2pYieldProxyFactory
    function getReferenceP2pYieldProxy() external view returns (address) {
        return address(i_referenceP2pYieldProxy);
    }

    /// @inheritdoc IP2pYieldProxyFactory
    function getHashForP2pSigner(
        address _client,
        uint96 _clientBasisPoints,
        uint256 _p2pSignerSigDeadline
    ) public view returns (bytes32) {
        return keccak256(abi.encode(
            _client,
            _clientBasisPoints,
            _p2pSignerSigDeadline,
            address(this),
            block.chainid
        ));
    }

    /// @inheritdoc IP2pYieldProxyFactory
    function getP2pSigner() external view returns (address) {
        return s_p2pSigner;
    }

    function getP2pOperator() public override(IP2pYieldProxyFactory, P2pOperator) view returns (address) {
        return super.getP2pOperator();
    }

    /// @inheritdoc IP2pYieldProxyFactory
    function getAllProxies() external view returns (address[] memory) {
        return s_allProxies;
    }

    /// @inheritdoc ERC165
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IP2pYieldProxyFactory).interfaceId ||
            super.supportsInterface(interfaceId);
    }
}
"
    },
    "src/adapters/resolv/p2pResolvProxy/P2pResolvProxy.sol": {
      "content": "// SPDX-FileCopyrightText: 2025 P2P Validator <info@p2p.org>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;

import "../../../@resolv/IResolvStaking.sol";
import "../../../@resolv/IStUSR.sol";
import "../../../@resolv/IStakedTokenDistributor.sol";
import "../../../p2pYieldProxy/P2pYieldProxy.sol";
import "./IP2pResolvProxy.sol";

error P2pResolvProxy__ZeroAddress_USR();
error P2pResolvProxy__AssetNotSupported(address _asset);
error P2pResolvProxy__UnauthorizedAccount(address _account);
error P2pResolvProxy__NotP2pOperator(address _caller);
error P2pResolvProxy__CallerNeitherClientNorP2pOperator(address _caller);
error P2pResolvProxy__ZeroAccruedRewards();

contract P2pResolvProxy is P2pYieldProxy, IP2pResolvProxy {
    using SafeERC20 for IERC20;

    /// @dev USR address
    address internal immutable i_USR;

    /// @dev stUSR address
    address internal immutable i_stUSR;

    /// @dev RESOLV address
    address internal immutable i_RESOLV;

    /// @dev stRESOLV address
    address internal immutable i_stRESOLV;

    IStakedTokenDistributor private immutable i_stakedTokenDistributor;

    /// @dev Throws if called by any account other than the P2pOperator.
    modifier onlyP2pOperator() {
        address p2pOperator = i_factory.getP2pOperator();
        require (msg.sender == p2pOperator, P2pResolvProxy__NotP2pOperator(msg.sender));
        _;
    }

    /// @dev Throws if called by any account other than client or P2pOperator.
    modifier onlyClientOrP2pOperator() {
        if (msg.sender != s_client) {
            address p2pOperator = i_factory.getP2pOperator();
            require (msg.sender == p2pOperator, P2pResolvProxy__CallerNeitherClientNorP2pOperator(msg.sender));
        }
        _;
    }

    /// @notice Constructor for P2pResolvProxy
    /// @param _factory Factory address
    /// @param _p2pTreasury P2pTreasury address
    /// @param _allowedCalldataChecker AllowedCalldataChecker
    /// @param _stUSR stUSR address
    /// @param _USR USR address
    /// @param _stRESOLV stRESOLV address
    /// @param _RESOLV RESOLV address
    /// @param _stakedTokenDistributor StakedTokenDistributor
    constructor(
        address _factory,
        address _p2pTreasury,
        address _allowedCalldataChecker,
        address _stUSR,
        address _USR,
        address _stRESOLV,
        address _RESOLV,
        address _stakedTokenDistributor
    ) P2pYieldProxy(_factory, _p2pTreasury, _allowedCalldataChecker) {
        require(_USR != address(0), P2pResolvProxy__ZeroAddress_USR());
        i_USR = _USR;

        i_stUSR = _stUSR;

        i_RESOLV = _RESOLV;

        i_stRESOLV = _stRESOLV;

        i_stakedTokenDistributor = IStakedTokenDistributor(_stakedTokenDistributor);
    }

    /// @inheritdoc IP2pYieldProxy
    function deposit(address _asset, uint256 _amount) external override {
        if (_asset == i_USR) {
            _deposit(
                i_stUSR,
                abi.encodeWithSelector(IStUSR.deposit.selector, _amount),
                i_USR,
                _amount
            );
        } else if (_asset == i_RESOLV) {
            _deposit(
                i_stRESOLV,
                abi.encodeWithSelector(IResolvStaking.deposit.selector, _amount, address(this)),
                i_RESOLV,
                _amount
            );
        } else {
            revert P2pResolvProxy__AssetNotSupported(_asset);
        }
    }

    /// @inheritdoc IP2pResolvProxy
    function withdrawUSR(uint256 _amount)
    external
    onlyClient {
        _withdraw(
            i_stUSR,
            i_USR,
            abi.encodeWithSelector(IStUSR.withdraw.selector, _amount)
        );
    }

    function withdrawUSRAccruedRewards()
    external
    onlyP2pOperator {
        int256 amount = calculateAccruedRewardsUSR();
        require (amount > 0, P2pResolvProxy__ZeroAccruedRewards());
        _withdraw(
            i_stUSR,
            i_USR,
            abi.encodeWithSelector(IStUSR.withdraw.selector, amount)
        );
    }

    /// @inheritdoc IP2pResolvProxy
    function withdrawAllUSR()
    external
    onlyClient {
        _withdraw(
            i_stUSR,
            i_USR,
            abi.encodeCall(IStUSR.withdrawAll, ())
        );
    }

    /// @inheritdoc IP2pResolvProxy
    function initiateWithdrawalRESOLV(uint256 _amount)
    external
    onlyClient {
        return IResolvStaking(i_stRESOLV).initiateWithdrawal(_amount);
    }

    function initiateWithdrawalRESOLVAccruedRewards()
    external
    onlyP2pOperator {
        int256 amount = calculateAccruedRewardsRESOLV();
        require (amount > 0, P2pResolvProxy__ZeroAccruedRewards());
        return IResolvStaking(i_stRESOLV).initiateWithdrawal(uint256(amount));
    }

    /// @inheritdoc IP2pResolvProxy
    function withdrawRESOLV()
    external
    onlyClientOrP2pOperator {
        bool isEnabled = IResolvStaking(i_stRESOLV).claimEnabled();

        _withdraw(
            i_stRESOLV,
            i_RESOLV,
            abi.encodeWithSelector(IResolvStaking.withdraw.selector, isEnabled, address(this))
        );
    }

    /// @inheritdoc IP2pResolvProxy
    function claimStakedTokenDistributor(
        uint256 _index,
        uint256 _amount,
        bytes32[] calldata _merkleProof
    )
    external
    nonReentrant
    {
        if (msg.sender != s_client) {
            address p2pOperator = i_factory.getP2pOperator();
            require(
                msg.sender == p2pOperator,
                P2pResolvProxy__UnauthorizedAccount(msg.sender)
            );
        }

        // claim _reward token from StakedTokenDistributor
        i_stakedTokenDistributor.claim(_index, _amount, _merkleProof);

        emit P2pResolvProxy__Claimed(_amount);
    }

    function getUserPrincipalUSR() public view returns(uint256) {
        return getUserPrincipal(i_USR);
    }

    function getUserPrincipalRESOLV() public view returns(uint256) {
        return getUserPrincipal(i_RESOLV);
    }

    function calculateAccruedRewardsUSR() public view returns(int256) {
        return calculateAccruedRewards(i_stUSR,i_USR);
    }

    function calculateAccruedRewardsRESOLV() public view returns(int256) {
        return calculateAccruedRewards(i_stRESOLV,i_RESOLV);
    }

    function getLastFeeCollectionTimeUSR() public view returns(uint48) {
        return getLastFeeCollectionTime(i_USR);
    }

    function getLastFeeCollectionTimeRESOLV() public view returns(uint48) {
        return getLastFeeCollectionTime(i_RESOLV);
    }

    /// @inheritdoc ERC165
    function supportsInterface(bytes4 interfaceId) public view virtual override(P2pYieldProxy) returns (bool) {
        return interfaceId == type(IP2pResolvProxy).interfaceId ||
            super.supportsInterface(interfaceId);
    }
}
"
    },
    "src/@openzeppelin/contracts/proxy/Clones.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/Clones.sol)

pragma solidity 0.8.30;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}
"
    },
    "src/@openzeppelin/contracts/utils/Address.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity 0.8.30;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                assembly ("memory-safe") {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
"
    },
    "src/@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity 0.8.30;

import "./ECDSA.sol";
import "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Gnosis Safe.
 *
 * _Available since v4.1._
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
        return
            (error == ECDSA.RecoverError.NoError && recovered == signer) ||
            isValidERC1271SignatureNow(signer, hash, signature);
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}
"
    },
    "src/@openzeppelin/contracts/utils/introspection/ERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity 0.8.30;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
"
    },
    "src/access/P2pOperator2Step.sol": {
      "content": "// SPDX-FileCopyrightText: 2025 P2P Validator <info@p2p.org>
// SPDX-License-Identifier: MIT

// Copy and rename of OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)

pragma solidity 0.8.30;

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);
    }
}
"
    },
    "src/common/AllowedCalldataChecker.sol": {
      "content": "// SPDX-FileCopyrightText: 2025 P2P Validator <info@p2p.org>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;

import "../@openzeppelin/contracts-upgradable/proxy/utils/Initializable.sol";
import "./IAllowedCalldataChecker.sol";

/// @dev No extra calls are allowed for now. AllowedCalldataChecker can be upgraded in the future.
error AllowedCalldataChecker__NoAllowedCalldata();

/// @title AllowedCalldataChecker
/// @author P2P Validator <info@p2p.org>
/// @notice Upgradable contract for checking if a calldata is allowed
contract AllowedCalldataChecker is IAllowedCalldataChecker, Initializable {

    function initialize() public initializer {
        // do nothing in this implementation
    }

    /// @inheritdoc IAllowedCalldataChecker
    function checkCalldata(
        address,
        bytes4,
        bytes calldata
    ) public pure {
        revert AllowedCalldataChecker__NoAllowedCalldata();
    }
}
"
    },
    "src/p2pYieldProxy/P2pYieldProxy.sol": {
      "content": "// SPDX-FileCopyrightText: 2025 P2P Validator <info@p2p.org>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;

import "../@openzeppelin/contracts-upgradable/security/ReentrancyGuardUpgradeable.sol";
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 "../common/AllowedCalldataChecker.sol";
import "../p2pYieldProxyFactory/IP2pYieldProxyFactory.sol";
import "../structs/P2pStructs.sol";
import "./IP2pYieldProxy.sol";
import {IERC4626} from "../@openzeppelin/contracts/interfaces/IERC4626.sol";

error P2pYieldProxy__ZeroAddressAsset();
error P2pYieldProxy__ZeroAssetAmount();
error P2pYieldProxy__ZeroSharesAmount();
error P2pYieldProxy__InvalidClientBasisPoints(uint96 _clientBasisPoints);
error P2pYieldProxy__NotFactory(address _factory);
error P2pYieldProxy__DifferentActuallyDepositedAmount(
    uint256 _requestedAmount,
    uint256 _actualAmount
);
error P2pYieldProxy__NotFactoryCalled(
    address _msgSender,
    IP2pYieldProxyFactory _actualFactory
);
error P2pYieldProxy__NotClientCalled(
    address _msgSender,
    address _actualClient
);
error P2pYieldProxy__ZeroAddressFactory();
error P2pYieldProxy__ZeroAddressP2pTreasury();
error P2pYieldProxy__ZeroAddressYieldProtocolAddress();
error P2pYieldProxy__ZeroAllowedCalldataChecker();
error P2pYieldProxy__DataTooShort();

/// @title P2pYieldProxy
/// @notice P2pYieldProxy is a contract that allows a client to deposit and withdraw assets from a yield protocol.
abstract contract P2pYieldProxy is
    Initializable,
    ReentrancyGuardUpgradeable,
    ERC165,
    IP2pYieldProxy {

    using SafeERC20 for IERC20;
    using Address for address;

    /// @dev P2pYieldProxyFactory
    IP2pYieldProxyFactory internal immutable i_factory;

    /// @dev P2pTreasury
    address internal immutable i_p2pTreasury;

    IAllowedCalldataChecker internal immutable i_allowedCalldataChecker;

    /// @dev Client
    address internal s_client;

    /// @dev Client basis points
    uint96 internal s_clientBasisPoints;

    // asset => amount
    mapping(address => uint256) internal s_totalDeposited;

    // asset => amount
    mapping(address => Withdrawn) internal s_totalWithdrawn;

    /// @notice If caller is not factory, revert
    modifier onlyFactory() {
        if (msg.sender != address(i_factory)) {
            revert P2pYieldProxy__NotFactoryCalled(msg.sender, i_factory);
        }
        _;
    }

    /// @notice If caller is not client, revert
    modifier onlyClient() {
        if (msg.sender != s_client) {
            revert P2pYieldProxy__NotClientCalled(msg.sender, s_client);
        }
        _;
    }

    /// @dev Modifier for checking if a calldata is allowed
    /// @param _yieldProtocolAddress The address of the yield protocol
    /// @param _yieldProtocolCalldata The calldata (encoded signature + arguments) to be passed to the yield protocol
    modifier calldataShouldBeAllowed(
        address _yieldProtocolAddress,
        bytes calldata _yieldProtocolCalldata
    ) {
        // validate yieldProtocolCalldata for yieldProtocolAddress
        bytes4 selector = _getFunctionSelector(_yieldProtocolCalldata);
        i_allowedCalldataChecker.checkCalldata(
            _yieldProtocolAddress,
            selector,
            _yieldProtocolCalldata[4:]
        );
        _;
    }

    /// @notice Constructor for P2pYieldProxy
    /// @param _factory The factory address
    /// @param _p2pTreasury The P2pTreasury address
    /// @param _allowedCalldataChecker AllowedCalldataChecker
    constructor(
        address _factory,
        address _p2pTreasury,
        address _allowedCalldataChecker
    ) {
        require(_factory != address(0), P2pYieldProxy__ZeroAddressFactory());
        i_factory = IP2pYieldProxyFactory(_factory);

        require(_p2pTreasury != address(0), P2pYieldProxy__ZeroAddressP2pTreasury());
        i_p2pTreasury = _p2pTreasury;

        require (_allowedCalldataChecker != address(0), P2pYieldProxy__ZeroAllowedCalldataChecker());
        i_allowedCalldataChecker = IAllowedCalldataChecker(_allowedCalldataChecker);
    }

    /// @inheritdoc IP2pYieldProxy
    function initialize(
        address _client,
        uint96 _clientBasisPoints
    )
    external
    initializer
    onlyFactory
    {
        __ReentrancyGuard_init();

        require(
            _clientBasisPoints > 0 && _clientBasisPoints <= 10_000,
            P2pYieldProxy__InvalidClientBasisPoints(_clientBasisPoints)
        );

        s_client = _client;
        s_clientBasisPoints = _clientBasisPoints;

        emit P2pYieldProxy__Initialized();
    }

    function deposit(address _asset, uint256 _amount) external virtual;

    /// @notice Deposit assets into yield protocol
    /// @param _yieldProtocolAddress yield protocol address
    /// @param _yieldProtocolDepositCalldata calldata for deposit function of yield protocol
    /// @param _asset asset to deposit
    /// @param _amount amount to deposit
    function _deposit(
        address _yieldProtocolAddress,
        bytes memory _yieldProtocolDepositCalldata,
        address _asset,
        uint256 _amount
    )
    internal
    onlyFactory
    {
        require (_asset != address(0), P2pYieldProxy__ZeroAddressAsset());
        require (_amount > 0, P2pYieldProxy__ZeroAssetAmount());

        address client = s_client;

        uint256 assetAmountBefore = IERC20(_asset).balanceOf(address(this));

        // transfer tokens into Proxy
        IERC20(_asset).safeTransferFrom(
            client,
            address(this),
            _amount
        );

        uint256 assetAmountAfter = IERC20(_asset).balanceOf(address(this));
        uint256 actualAmount = assetAmountAfter - assetAmountBefore;

        require (
            actualAmount == _amount,
            P2pYieldProxy__DifferentActuallyDepositedAmount(_amount, actualAmount)
        ); // no support for fee-on-transfer or rebasing tokens

        uint256 totalDepositedAfter = s_totalDeposited[_asset] + actualAmount;
        s_totalDeposited[_asset] = totalDepositedAfter;
        emit P2pYieldProxy__Deposited(
            _yieldProtocolAddress,
            _asset,
            actualAmount,
            totalDepositedAfter
        );

        IERC20(_asset).safeIncreaseAllowance(
            _yieldProtocolAddress,
            actualAmount
        );

        _yieldProtocolAddress.functionCall(_yieldProtocolDepositCalldata);
    }

    /// @notice Withdraw assets from yield protocol
    /// @param _yieldProtocolAddress yield protocol address
    /// @param _asset ERC-20 asset address
    /// @param _yieldProtocolWithdrawalCalldata calldata for withdraw function of yield protocol
    function _withdraw(
        address _yieldProtocolAddress,
        address _asset,
        bytes memory _yieldProtocolWithdrawalCalldata
    )
    internal
    nonReentrant
    {
        int256 accruedRewards = calculateAccruedRewards(_yieldProtocolAddress, _asset);

        uint256 assetAmountBefore = IERC20(_asset).balanceOf(address(this));

        // withdraw assets from Protocol
        _yieldProtocolAddress.functionCall(_yieldProtocolWithdrawalCalldata);

        uint256 assetAmountAfter = IERC20(_asset).balanceOf(address(this));

        uint256 newAssetAmount = assetAmountAfter - assetAmountBefore;

        Withdrawn memory withdrawn = s_totalWithdrawn[_asset];
        uint256 totalWithdrawnBefore = uint256(withdrawn.amount);
        uint256 totalWithdrawnAfter = totalWithdrawnBefore + newAssetAmount;

        // update total withdrawn
        withdrawn.amount = uint208(totalWithdrawnAfter);
        withdrawn.lastFeeCollectionTime = uint48(block.timestamp);
        s_totalWithdrawn[_asset] = withdrawn;

        uint256 p2pAmount;
        if (accruedRewards > 0) {
            // That extra 9999 ensures that any nonzero remainder will push the result up by 1 (ceiling division).
            p2pAmount = (uint256(accruedRewards) * (10_000 - s_clientBasisPoints) + 9999) / 10_000;
        }
        uint256 clientAmount = newAssetAmount - p2pAmount;

        if (p2pAmount > 0) {
            IERC20(_asset).safeTransfer(i_p2pTreasury, p2pAmount);
        }
        // clientAmount must be > 0 at this point
        IERC20(_asset).safeTransfer(s_client, clientAmount);

        emit P2pYieldProxy__Withdrawn(
            _yieldProtocolAddress,
            _yieldProtocolAddress,
            _asset,
            newAssetAmount,
            totalWithdrawnAfter,
            accruedRewards,
            p2pAmount,
            clientAmount
        );
    }

    /// @inheritdoc IP2pYieldProxy
    function callAnyFunction(
        address _yieldProtocolAddress,
        bytes calldata _yieldProtocolCalldata
    )
    external
    onlyClient
    nonReentrant
    calldataShouldBeAllowed(_yieldProtocolAddress, _yieldProtocolCalldata)
    {
        emit P2pYieldProxy__CalledAsAnyFunction(_yieldProtocolAddress);
        _yieldProtocolAddress.functionCall(_yieldProtocolCalldata);
    }

    /// @notice Returns function selector (first 4 bytes of data)
    /// @param _data calldata (encoded signature + arguments)
    /// @return functionSelector function selector
    function _getFunctionSelector(
        bytes calldata _data
    ) private pure returns (bytes4 functionSelector) {
        require (_data.length >= 4, P2pYieldProxy__DataTooShort());
        return bytes4(_data[:4]);
    }

    /// @inheritdoc IP2pYieldProxy
    function getFactory() external view returns (address) {
        return address(i_factory);
    }

    /// @inheritdoc IP2pYieldProxy
    function getP2pTreasury() external view returns (address) {
        return i_p2pTreasury;
    }

    /// @inheritdoc IP2pYieldProxy
    function getClient() external view returns (address) {
        return s_client;
    }

    /// @inheritdoc IP2pYieldProxy
    function getClientBasisPoints() external view returns (uint96) {
        return s_clientBasisPoints;
    }

    /// @inheritdoc IP2pYieldProxy
    function getTotalDeposited(address _asset) external view returns (uint256) {
        return s_totalDeposited[_asset];
    }

    /// @inheritdoc IP2pYieldProxy
    function getTotalWithdrawn(address _asset) external view returns (uint256) {
        return s_totalWithdrawn[_asset].amount;
    }

    function getUserPrincipal(address _asset) public view returns(uint256) {
        uint256 totalDeposited = s_totalDeposited[_asset];
        uint256 totalWithdrawn = s_totalWithdrawn[_asset].amount;
        if (totalDeposited > totalWithdrawn) {
            return totalDeposited - totalWithdrawn;
        }
        return 0;
    }

    function calculateAccruedRewards(address _yieldProtocolAddress, address _asset) public view returns(int256) {
        uint256 currentAmount = IERC20(_yieldProtocolAddress).balanceOf(address(this));
        uint256 userPrincipal = getUserPrincipal(_asset);
        return int256(currentAmount) - int256(userPrincipal);
    }

    function getLastFeeCollectionTime(address _asset) public view returns(uint48) {
        return s_totalWithdrawn[_asset].lastFeeCollectionTime;
    }

    /// @inheritdoc ERC165
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IP2pYieldProxy).interfaceId ||
            super.supportsInterface(interfaceId);
    }
}
"
    },
    "src/p2pYieldProxyFactory/IP2pYieldProxyFactory.sol": {
      "content": "// SPDX-FileCopyrightText: 2025 P2P Validator <info@p2p.org>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;

import "../@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../common/IAllowedCalldataChecker.sol";

/// @dev External interface of P2pYieldProxyFactory
interface IP2pYieldProxyFactory is IAllowedCalldataChecker, IERC165 {

    /// @dev Emitted when the P2pSigner is transferred
    event P2pYieldProxyFactory__P2pSignerTransferred(
        address indexed _previousP2pSigner,
        address indexed _newP2pSigner
    );

    /// @dev Emitted when the deposit is made
    event P2pYieldProxyFactory__Deposited(
        address indexed _client,
        uint96 indexed _clientBasisPoints
    );

    /// @dev Emitted when the a new proxy is created
    event P2pYieldProxyFactory__ProxyCreated(
        address _proxy,
        address _client,
        uint96 _clientBasisPoints
    );

    /// @dev Deposits the yield protocol
    /// @param _asset asset
    /// @param _amount amount
    /// @param _clientBasisPoints The client basis points
    /// @param _p2pSignerSigDeadline The P2pSigner signature deadline
    /// @param _p2pSignerSignature The P2pSigner signature
    /// @return p2pYieldProxyAddress The client's P2pYieldProxy instance address
    function deposit(
        address _asset,
        uint256 _amount,

        uint96 _clientBasisPoints,
        uint256 _p2pSignerSigDeadline,
        bytes calldata _p2pSignerSignature
    )
    external
    returns (address p2pYieldProxyAddress);

    /// @dev Computes the address of a P2pYieldProxy created by `_createP2pYieldProxy` function
    /// @dev P2pYieldProxy instances are guaranteed to have the same address if _feeDistributorInstance is the same
    /// @param _client The address of client
    /// @return address The address of the P2pYieldProxy instance
    function predictP2pYieldProxyAddress(
        address _client,
        uint96 _clientBasisPoints
    ) external view returns (address);

    /// @dev Transfers the P2pSigner
    /// @param _newP2pSigner The new P2pSigner address
    function transferP2pSigner(
        address _newP2pSigner
    ) external;

    /// @dev Returns a template set by P2P to be used for new P2pYieldProxy instances
    /// @return a template set by P2P to be used for new P2pYieldProxy instances
    function getReferenceP2pYieldProxy() external view returns (address);

    /// @dev Gets the hash for the P2pSigner
    /// @param _client The address of client
    /// @param _clientBasisPoints The client basis points
    /// @param _p2pSignerSigDeadline The P2pSigner signature deadline
    /// @return The hash for the P2pSigner
    function getHashForP2pSigner(
        address _client,
        uint96 _clientBasisPoints,
        uint256 _p2pSignerSigDeadline
    ) external view returns (bytes32);

    /// @dev Gets the P2pSigner
    /// @return The P2pSigner address
    function getP2pSigner() external view returns (address);

    function getP2pOperator() external view returns (address);

    /// @dev Gets all proxies
    /// @return The proxy addresses
    function getAllProxies() external view returns (address[] memory);
}
"
    },
    "src/@resolv/IResolvStaking.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

interface IResolvStaking {

    function deposit(
        uint256 _amount,
        address _receiver
    ) external;

    function withdraw(
        bool _claimRewards,
        address _receiver
    ) external;

    function initiateWithdrawal(uint256 _amount) external;

    function claim(address _user, address _receiver) external;

    function updateCheckpoint(address _user) external;

    function depositReward(
        address _token,
        uint256 _amount,
        uint256 _duration
    ) external;

    function setRewardsReceiver(address _receiver) external;

    function setCheckpointDelegatee(address _delegatee) external;

    function setClaimEnabled(bool _enabled) external;

    function setWithdrawalCooldown(uint256 _cooldown) external;

    function getUserAccumulatedRewardPerToken(address _user, address _token) external view returns (uint256 amount);

    function getUserClaimableAmounts(address _user, address _token) external view returns (uint256 amount);

    function getUserEffectiveBalance(address _user) external view returns (uint256 balance);

    function claimEnabled() external view returns (bool isEnabled);
}
"
    },
    "src/@resolv/IStUSR.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

interface IStUSR {

    event Deposit(address indexed _sender, address indexed _receiver, uint256 _usrAmount, uint256 _shares);
    event Withdraw(address indexed _sender, address indexed _receiver, uint256 _usrAmount, uint256 _shares);

    error InvalidDepositAmount(uint256 _usrAmount);

    function deposit(uint256 _usrAmount) external;

    function withdraw(uint256 _usrAmount) external;

    function withdrawAll() external;

    function previewDeposit(uint256 _usrAmount) external view returns (uint256 shares);

    function previewWithdraw(uint256 _usrAmount) external view returns (uint256 shares);
}"
    },
    "src/@resolv/IStakedTokenDistributor.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

// Allows anyone to claim a token if they exist in a merkle root.
interface IStakedTokenDistributor {
    event Claimed(uint256 index, address account, uint256 amount);
    event AddedToBlacklist(address account);
    event RemovedFromBlacklist(address account);
    event Withdrawn(address reciever);

    error AlreadyClaimed();
    error InvalidProof();
    error Blacklisted();
    error EndTimeInPast();
    error ClaimWindowFinished();
    error NoWithdrawDuringClaim();
    error ZeroAddress();

    // Claim the given amount of the token to the contract caller. Reverts if the inputs are invalid.
    function claim(uint256 index, uint256 amount, bytes32[] calldata merkleProof) external;
    // Returns true if the index has been marked claimed.
    function isClaimed(uint256 index) external view returns (bool);
}"
    },
    "src/adapters/resolv/p2pResolvProxy/IP2pResolvProxy.sol": {
      "content": "// SPDX-FileCopyrightText: 2025 P2P Validator <info@p2p.org>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.30;

interface IP2pResolvProxy {
    function withdrawUSR(uint256 _amount) external;

    function withdrawAllUSR() external;

    function initiateWithdrawalRESOLV(uint256 _amount) external;

    function withdrawRESOLV() external;

    function claimStakedTokenDistributor(
        uint256 _index,
        uint256 _amount,
        bytes32[] calldata _merkleProof
    )
    external;

    event P2pResolvProxy__Claimed(uint256 _amount);
}
"
    },
    "src/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity 0.8.30;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\
32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\
", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataW

Tags:
ERC20, ERC165, Proxy, Mintable, Yield, Upgradeable, Factory|addr:0xa8742636b5945545d77f5f6ea32f470c3a3ad869|verified:true|block:23632627|tx:0x3a229d78be25a4cdac9866bfce72336a3407824ffa42cd85ead9d4839cef16c4|first_check:1761241338

Submitted on: 2025-10-23 19:42:21

Comments

Log in to comment.

No comments yet.