Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// @notice This contract composes both facets so a single deployment exposes the full interface.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity =0.8.28;

import {AppStorage} from "./AccountConfigFacets/AppStorage.sol";
import {
Expand All @@ -16,6 +16,8 @@ import {IDiamondLoupe} from "../interfaces/IDiamondLoupe.sol";

// When no function exists for function called
error FunctionNotFound(bytes4 _functionSelector);
// When ETH is sent directly to the contract
error DirectETHTransferNotAllowed();

contract AccountConfig {
using EnumerableSet for EnumerableSet.AddressSet;
Expand All @@ -37,6 +39,11 @@ contract AccountConfig {
s.requestedApiPayerCount = 3; // just a default for spinning up a new instance
}

/// @notice Reject any ETH sent directly to the contract.
receive() external payable {
revert DirectETHTransferNotAllowed();
}
Comment thread
GTC6244 marked this conversation as resolved.

// Find facet for function that is called and execute the
// function if a facet is found and return any value.
fallback() external {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// @notice Process API calls for AccountConfig diamond.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity =0.8.28;

import {
EnumerableSet
Expand All @@ -15,10 +15,17 @@ contract APIConfigFacet {
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableSet for EnumerableSet.AddressSet;

event ConfigOperatorUpdated(address indexed newConfigOperator);
event ApiPayersUpdated(address[] newApiPayers);
event AdminApiPayerUpdated(address indexed newAdminApiPayerAccount);
event RebalanceAmountUpdated(uint256 newRebalanceAmount);
event RequestedApiPayerCountUpdated(uint256 newCount);

function setConfigOperator(address newConfigOperator) public {
SecurityLib.revertIfNotConfigOperatorOrOwner(msg.sender);
AppStorage.AccountConfigStorage storage s = AppStorage.getStorage();
s.configOperator = newConfigOperator;
emit ConfigOperatorUpdated(newConfigOperator);
}

function setRequestedApiPayerCount(
Expand All @@ -27,17 +34,20 @@ contract APIConfigFacet {
SecurityLib.revertIfNotApiPayerOrOwner(msg.sender);
AppStorage.AccountConfigStorage storage s = AppStorage.getStorage();
s.requestedApiPayerCount = newRequestedApiPayerCount;
emit RequestedApiPayerCountUpdated(newRequestedApiPayerCount);
}

function setAdminApiPayerAccount(address newAdminApiPayerAccount) public {
SecurityLib.revertIfNotApiPayerOrOwner(msg.sender);
AppStorage.AccountConfigStorage storage s = AppStorage.getStorage();
s.adminApiPayerAccount = newAdminApiPayerAccount;
emit AdminApiPayerUpdated(newAdminApiPayerAccount);
}

// setApiPayers is used to add new signers (accounts that pay for state mutation made by api calls) to the list of api payers.
// Restricted to owner + admin API payer (not regular API payers) to prevent hostile payer takeover.
function setApiPayers(address[] memory newApiPayers) public {
SecurityLib.revertIfNotApiPayerOrOwner(msg.sender);
SecurityLib.revertIfNotOwnerOrAdminApiPayer(msg.sender);

AppStorage.AccountConfigStorage storage s = AppStorage.getStorage();

Expand All @@ -46,11 +56,13 @@ contract APIConfigFacet {
for (uint256 i = 0; i < newApiPayers.length; i++) {
s.api_payers.add(newApiPayers[i]);
}
emit ApiPayersUpdated(newApiPayers);
}

function setRebalanceAmount(uint256 newRebalanceAmount) public {
SecurityLib.revertIfNotApiPayerOrOwner(msg.sender);
AppStorage.AccountConfigStorage storage s = AppStorage.getStorage();
s.rebalanceAmount = newRebalanceAmount;
emit RebalanceAmountUpdated(newRebalanceAmount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/// @notice Each struct contains a keccak256 that matches the mapping to prove existence of the struct.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity =0.8.28;

import {
EnumerableSet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// @notice Stripe is currently used to manage billing.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity =0.8.28;

import {
EnumerableSet
Expand All @@ -16,6 +16,11 @@ contract BillingFacet {
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableSet for EnumerableSet.AddressSet;

event ApiKeyDebited(uint256 indexed apiKeyHash, uint256 amount);
event ApiKeyCredited(uint256 indexed apiKeyHash, uint256 amount);
event PricingUpdated(uint256 indexed pricingItemId, uint256 price);
event PricingOperatorUpdated(address indexed newPricingOperator);

function getPricing(uint256 pricingItemId) public view returns (uint256) {
return AppStorage.getStorage().pricing[pricingItemId];
}
Expand All @@ -25,45 +30,33 @@ contract BillingFacet {
SecurityLib.revertIfNoAccountAccess(apiKeyHash, msg.sender);
SecurityLib.revertIfNotMasterAccount(apiKeyHash);
AppStorage.AccountConfigStorage storage s = AppStorage.getStorage();
uint256 masterAccountApiKeyHash = s.allApiKeyHashesToMaster[apiKeyHash];
AppStorage.Account storage account = s.accounts[
masterAccountApiKeyHash
];
AppStorage.UsageApiKey storage usageApiKey = (account
.accountApiKey
.apiKeyHash == apiKeyHash)
? account.accountApiKey
: account.usageApiKeys[apiKeyHash];
if (usageApiKey.balance < amount) {
AppStorage.Account storage account = s.accounts[apiKeyHash];
if (account.accountApiKey.balance < amount) {
revert AppStorage.InsufficientBalance(apiKeyHash, amount);
}
usageApiKey.balance -= amount;
account.accountApiKey.balance -= amount;
emit ApiKeyDebited(apiKeyHash, amount);
}

function creditApiKey(uint256 apiKeyHash, uint256 amount) public {
SecurityLib.revertIfNotApiPayerOrPricingOperator(msg.sender);
SecurityLib.revertIfNoAccountAccess(apiKeyHash, msg.sender);
SecurityLib.revertIfNotMasterAccount(apiKeyHash);
AppStorage.AccountConfigStorage storage s = AppStorage.getStorage();
uint256 masterAccountApiKeyHash = s.allApiKeyHashesToMaster[apiKeyHash];
AppStorage.Account storage account = s.accounts[
masterAccountApiKeyHash
];
AppStorage.UsageApiKey storage usageApiKey = (account
.accountApiKey
.apiKeyHash == apiKeyHash)
? account.accountApiKey
: account.usageApiKeys[apiKeyHash];
usageApiKey.balance += amount;
AppStorage.Account storage account = s.accounts[apiKeyHash];
account.accountApiKey.balance += amount;
emit ApiKeyCredited(apiKeyHash, amount);
}

function setPricing(uint256 pricingItemId, uint256 price) public {
SecurityLib.revertIfNotApiPayerOrPricingOperator(msg.sender);
AppStorage.getStorage().pricing[pricingItemId] = price;
emit PricingUpdated(pricingItemId, price);
}

function setPricingOperator(address newPricingOperator) public {
SecurityLib.revertIfNotApiPayerOrPricingOperator(msg.sender);
SecurityLib.revertIfNotOwner(msg.sender);
AppStorage.getStorage().pricingOperator = newPricingOperator;
emit PricingOperatorUpdated(newPricingOperator);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity =0.8.28;

/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,35 @@
/// @notice Security functions for AccountConfig diamond.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity =0.8.28;

import {
EnumerableSet
} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {AppStorage} from "./AppStorage.sol";
import {LibDiamond} from "../../libraries/LibDiamond.sol";
import {LibDiamond, NotContractOwner} from "../../libraries/LibDiamond.sol";

library SecurityLib {
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableSet for EnumerableSet.AddressSet;

// ----- internal view helpers (revert helpers call view from Views facet conceptually; we duplicate for simplicity) -----
function revertIfNotOwner(address caller) internal view {
if (caller != LibDiamond.contractOwner()) {
revert NotContractOwner(caller, LibDiamond.contractOwner());
}
}

function revertIfNotOwnerOrAdminApiPayer(address caller) internal view {
AppStorage.AccountConfigStorage storage s = AppStorage.getStorage();
if (
caller != LibDiamond.contractOwner() &&
caller != s.adminApiPayerAccount
) {
revert AppStorage.OnlyApiPayerOrOwner(caller);
}
}

function revertIfNotMasterAccount(uint256 accountApiKeyHash) internal view {
AppStorage.AccountConfigStorage storage s = AppStorage.getStorage();
if (s.allApiKeyHashesToMaster[accountApiKeyHash] != accountApiKeyHash) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// @notice View (read-only) functions for AccountConfig diamond.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity =0.8.28;

import {
EnumerableSet
Expand Down
Loading
Loading