Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Deoracleized #42

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 65 additions & 37 deletions contracts/feeDistributor/BaseFeeDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ import "../lib/P2pAddressLib.sol";
import "./Erc4337Account.sol";

/// @title Common logic for all FeeDistributor types
abstract contract BaseFeeDistributor is Erc4337Account, OwnableTokenRecoverer, OwnableWithOperator, ReentrancyGuard, ERC165, IFeeDistributor {

abstract contract BaseFeeDistributor is
Erc4337Account,
OwnableTokenRecoverer,
OwnableWithOperator,
ReentrancyGuard,
ERC165,
IFeeDistributor
{
/// @notice FeeDistributorFactory address
IFeeDistributorFactory internal immutable i_factory;

Expand Down Expand Up @@ -51,11 +57,13 @@ abstract contract BaseFeeDistributor is Erc4337Account, OwnableTokenRecoverer, O
/// @dev Set values that are constant, common for all the clients, known at the initial deploy time.
/// @param _factory address of FeeDistributorFactory
/// @param _service address of the service (P2P) fee recipient
constructor(
address _factory,
address payable _service
) {
if (!ERC165Checker.supportsInterface(_factory, type(IFeeDistributorFactory).interfaceId)) {
constructor(address _factory, address payable _service) {
if (
!ERC165Checker.supportsInterface(
_factory,
type(IFeeDistributorFactory).interfaceId
)
) {
revert FeeDistributor__NotFactory(_factory);
}
if (_service == address(0)) {
Expand All @@ -80,39 +88,29 @@ abstract contract BaseFeeDistributor is Erc4337Account, OwnableTokenRecoverer, O
revert FeeDistributor__ZeroAddressClient();
}
if (_clientConfig.recipient == i_service) {
revert FeeDistributor__ClientAddressEqualsService(_clientConfig.recipient);
revert FeeDistributor__ClientAddressEqualsService(
_clientConfig.recipient
);
}
if (s_clientConfig.recipient != address(0)) {
revert FeeDistributor__ClientAlreadySet(s_clientConfig.recipient);
}
if (_clientConfig.basisPoints >= 10000) {
revert FeeDistributor__InvalidClientBasisPoints(_clientConfig.basisPoints);
}

if (_referrerConfig.recipient != address(0)) {// if there is a referrer
if (_referrerConfig.recipient != address(0)) {
// if there is a referrer
if (_referrerConfig.recipient == i_service) {
revert FeeDistributor__ReferrerAddressEqualsService(_referrerConfig.recipient);
revert FeeDistributor__ReferrerAddressEqualsService(
_referrerConfig.recipient
);
}
if (_referrerConfig.recipient == _clientConfig.recipient) {
revert FeeDistributor__ReferrerAddressEqualsClient(_referrerConfig.recipient);
}
if (_referrerConfig.basisPoints == 0) {
revert FeeDistributor__ZeroReferrerBasisPointsForNonZeroReferrer();
}
if (_clientConfig.basisPoints + _referrerConfig.basisPoints > 10000) {
revert FeeDistributor__ClientPlusReferralBasisPointsExceed10000(
_clientConfig.basisPoints,
_referrerConfig.basisPoints
revert FeeDistributor__ReferrerAddressEqualsClient(
_referrerConfig.recipient
);
}

// set referrer config
s_referrerConfig = _referrerConfig;

} else {// if there is no referrer
if (_referrerConfig.basisPoints != 0) {
revert FeeDistributor__ReferrerBasisPointsMustBeZeroIfAddressIsZero(_referrerConfig.basisPoints);
}
}

// set client config
Expand All @@ -125,14 +123,25 @@ abstract contract BaseFeeDistributor is Erc4337Account, OwnableTokenRecoverer, O
_referrerConfig.basisPoints
);

bool clientCanReceiveEther = P2pAddressLib._sendValue(_clientConfig.recipient, 0);
bool clientCanReceiveEther = P2pAddressLib._sendValue(
_clientConfig.recipient,
0
);
if (!clientCanReceiveEther) {
revert FeeDistributor__ClientCannotReceiveEther(_clientConfig.recipient);
revert FeeDistributor__ClientCannotReceiveEther(
_clientConfig.recipient
);
}
if (_referrerConfig.recipient != address(0)) {// if there is a referrer
bool referrerCanReceiveEther = P2pAddressLib._sendValue(_referrerConfig.recipient, 0);
if (_referrerConfig.recipient != address(0)) {
// if there is a referrer
bool referrerCanReceiveEther = P2pAddressLib._sendValue(
_referrerConfig.recipient,
0
);
if (!referrerCanReceiveEther) {
revert FeeDistributor__ReferrerCannotReceiveEther(_referrerConfig.recipient);
revert FeeDistributor__ReferrerCannotReceiveEther(
_referrerConfig.recipient
);
}
}
}
Expand All @@ -156,7 +165,12 @@ abstract contract BaseFeeDistributor is Erc4337Account, OwnableTokenRecoverer, O
}

/// @inheritdoc IFeeDistributor
function client() public view override(Erc4337Account, IFeeDistributor) returns (address) {
function client()
public
view
override(Erc4337Account, IFeeDistributor)
returns (address)
{
return s_clientConfig.recipient;
}

Expand All @@ -176,17 +190,31 @@ abstract contract BaseFeeDistributor is Erc4337Account, OwnableTokenRecoverer, O
}

/// @inheritdoc ERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IFeeDistributor).interfaceId || super.supportsInterface(interfaceId);
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IFeeDistributor).interfaceId ||
super.supportsInterface(interfaceId);
}

/// @inheritdoc IOwnable
function owner() public view override(Erc4337Account, OwnableBase, Ownable, IOwnable) returns (address) {
function owner()
public
view
override(Erc4337Account, OwnableBase, Ownable, IOwnable)
returns (address)
{
return i_factory.owner();
}

/// @inheritdoc IOwnableWithOperator
function operator() public view override(Erc4337Account, OwnableWithOperator) returns (address) {
function operator()
public
view
override(Erc4337Account, OwnableWithOperator)
returns (address)
{
return super.operator();
}
}
88 changes: 88 additions & 0 deletions contracts/feeDistributor/DeoracleizedFeeDistributor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: 2024 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import "../@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "../@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "../feeDistributorFactory/IFeeDistributorFactory.sol";
import "../assetRecovering/OwnableTokenRecoverer.sol";
import "./IFeeDistributor.sol";
import "../structs/P2pStructs.sol";
import "./BaseFeeDistributor.sol";

/// @title FeeDistributor
contract DeoracleizedFeeDistributor is BaseFeeDistributor {

Check failure on line 16 in contracts/feeDistributor/DeoracleizedFeeDistributor.sol

View workflow job for this annotation

GitHub Actions / Check Spelling

`Deoracleized` is not a recognized word. (unrecognized-spelling)
/// @dev Set values that are constant, common for all the clients, known at the initial deploy time.
/// @param _factory address of FeeDistributorFactory
/// @param _service address of the service (P2P) fee recipient
constructor(
address _factory,
address payable _service
) BaseFeeDistributor(_factory, _service) {}

/// @notice Withdraw
function withdraw(Withdrawal calldata _withdrawal) external nonReentrant {
i_factory.checkOperatorOrOwner(msg.sender);

if (s_clientConfig.recipient == address(0)) {
revert FeeDistributor__ClientNotSet();
}

if (address(this).balance == 0) {
// revert if there is no ether to withdraw
revert FeeDistributor__NothingToWithdraw();
}

uint256 clientAmount = uint256(_withdrawal.clientAmount);
uint256 serviceAmount = uint256(_withdrawal.serviceAmount);
uint256 referrerAmount = uint256(_withdrawal.referrerAmount);

if (
clientAmount + serviceAmount + referrerAmount >
address(this).balance
) {
revert FeeDistributor__AmountsExceedBalance();
}

if (clientAmount + serviceAmount + referrerAmount == 0) {
revert FeeDistributor__AmountsAreZero();
}

if (referrerAmount > 0) {
if (s_referrerConfig.recipient != address(0)) {
// if there is a referrer

// Send ETH to referrer. Ignore the possible yet unlikely revert in the receive function.
P2pAddressLib._sendValue(
s_referrerConfig.recipient,
referrerAmount
);
} else {
revert FeeDistributor__ReferrerNotSet();
}
}

if (serviceAmount > 0) {
// Send ETH to service. Ignore the possible yet unlikely revert in the receive function.
P2pAddressLib._sendValue(i_service, serviceAmount);
}

if (clientAmount > 0) {
// Send ETH to client. Ignore the possible yet unlikely revert in the receive function.
P2pAddressLib._sendValue(s_clientConfig.recipient, clientAmount);
}

emit FeeDistributor__Withdrawn(
serviceAmount,
clientAmount,
referrerAmount
);
}

/// @inheritdoc Erc4337Account
function withdrawSelector() public pure override returns (bytes4) {
return DeoracleizedFeeDistributor.withdraw.selector;
}
}
34 changes: 14 additions & 20 deletions contracts/feeDistributor/FeeDistributorErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,6 @@ error FeeDistributor__ClientAddressEqualsService(address _passedAddress);
/// @notice Client address should be an actual client address, not zero.
error FeeDistributor__ZeroAddressClient();

/// @notice Client basis points should be >= 0 and <= 10000
/// @param _clientBasisPoints passed incorrect client basis points
error FeeDistributor__InvalidClientBasisPoints(uint96 _clientBasisPoints);

/// @notice Referrer basis points should be > 0 if the referrer exists
error FeeDistributor__ZeroReferrerBasisPointsForNonZeroReferrer();

/// @notice The sum of (Client basis points + Referral basis points) should be >= 0 and <= 10000
/// @param _clientBasisPoints passed client basis points
/// @param _referralBasisPoints passed referral basis points
error FeeDistributor__ClientPlusReferralBasisPointsExceed10000(uint96 _clientBasisPoints, uint96 _referralBasisPoints);

/// @notice Referrer address should be different from service address.
/// @param _passedAddress passed referrer address that equals to the service address
error FeeDistributor__ReferrerAddressEqualsService(address _passedAddress);
Expand All @@ -42,7 +30,10 @@ error FeeDistributor__ReferrerAddressEqualsClient(address _passedAddress);
/// @notice Only factory can call `initialize`.
/// @param _msgSender sender address.
/// @param _actualFactory the actual factory address that can call `initialize`.
error FeeDistributor__NotFactoryCalled(address _msgSender, IFeeDistributorFactory _actualFactory);
error FeeDistributor__NotFactoryCalled(
address _msgSender,
IFeeDistributorFactory _actualFactory
);

/// @notice `initialize` should only be called once.
/// @param _existingClient address of the client with which the contact has already been initialized.
Expand All @@ -52,9 +43,15 @@ error FeeDistributor__ClientAlreadySet(address _existingClient);
/// @dev The client address is supposed to be set by the factory.
error FeeDistributor__ClientNotSet();

/// @notice basisPoints of the referrer must be zero if referrer address is empty.
/// @param _referrerBasisPoints basisPoints of the referrer.
error FeeDistributor__ReferrerBasisPointsMustBeZeroIfAddressIsZero(uint96 _referrerBasisPoints);
/// @notice Cannot call `withdraw` if the referrer address is not set yet.
/// @dev The referrer address is supposed to be set by the factory.
error FeeDistributor__ReferrerNotSet();

/// @notice Sum of client, service and referrer amounts exceeds balance.
error FeeDistributor__AmountsExceedBalance();

/// @notice All amounts are zero.
error FeeDistributor__AmountsAreZero();

/// @notice service should be able to receive ether.
/// @param _service address of the service.
Expand All @@ -79,10 +76,7 @@ error FeeDistributor__CallerNotClient(address _caller, address _client);
/// @notice Throws in case there was some ether left after `withdraw` and it has failed to recover.
/// @param _to destination address for ether.
/// @param _amount how much wei the destination address should have received, but didn't.
error FeeDistributor__EtherRecoveryFailed(
address _to,
uint256 _amount
);
error FeeDistributor__EtherRecoveryFailed(address _to, uint256 _amount);

/// @notice ETH receiver should not be a zero address
error FeeDistributor__ZeroAddressEthReceiver();
10 changes: 10 additions & 0 deletions contracts/structs/P2pStructs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,13 @@ struct ClientDeposit {
ClientDepositStatus status;
uint96 ethAmountPerValidatorInWei;
}

/// @dev 256 bit struct
/// @member clientAmount ETH in wei to be sent to the client
/// @member serviceAmount ETH in wei to be sent to the service
/// @member referrerAmount ETH in wei to be sent to the referrer
struct Withdrawal {
uint80 clientAmount;
uint80 serviceAmount;
uint80 referrerAmount;
}
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
test = 'test/foundry'
cache_path = 'forge-cache'
solc-version = "0.8.24"
evm_version = 'cancun'

Check failure on line 8 in foundry.toml

View workflow job for this annotation

GitHub Actions / Check Spelling

`cancun` is not a recognized word. (unrecognized-spelling)
via_ir = true
optimizer = true
optimizer-runs = 200
gas_limit = "18446744073709551615"

rpc_endpoints = { mainnet = "https://rpc.ankr.com/eth", holesky = "https://rpc.ankr.com/eth_holesky" }

Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@
"sendErc4337UserOperation": "yarn hardhat run --network holesky scripts/sendErc4337UserOperation.ts",
"sendErc4337UserOperationForOracleFeeDistributor": "yarn hardhat run --network holesky scripts/sendErc4337UserOperationForOracleFeeDistributor.ts",
"generateMockMerkleTree": "yarn hardhat run --network holesky scripts/generateMockMerkleTree.ts",
"deployWithForge": "forge script script/Deploy.s.sol:Deploy --rpc-url $GOERLI_RPC_URL --private-key $PRIVATE_KEY --broadcast --chain holesky --json --verify --etherscan-api-key $ETHERSCAN_API_KEY -vvvv"
"deployWithForge": "forge script script/Deploy.s.sol:Deploy --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --chain holesky --json --verify --etherscan-api-key $ETHERSCAN_API_KEY -vvvvv"
},
"dependencies": {
"@google-cloud/bigquery": "6.2.0"
}
}
},
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
Loading
Loading