Skip to content

Commit

Permalink
Additional even emitting + sanity checks + version (#10419)
Browse files Browse the repository at this point in the history
* Additional even emitting + sanity checks + version

* Cherry picked changes from bugfix/MERC-1618

* Wrappers + gas

* Fixed issue with getAvailableRewardPoolIds

---------

Co-authored-by: Austin Born <[email protected]>
  • Loading branch information
Fletch153 and austinborn authored Sep 6, 2023
1 parent 1176a8c commit 2ff674b
Show file tree
Hide file tree
Showing 19 changed files with 646 additions and 283 deletions.
332 changes: 171 additions & 161 deletions contracts/gas-snapshots/llo-feeds.gas-snapshot

Large diffs are not rendered by default.

15 changes: 12 additions & 3 deletions contracts/src/v0.8/llo-feeds/VerifierProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {IVerifier} from "./interfaces/IVerifier.sol";
import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol";
import {AccessControllerInterface} from "../shared/interfaces/AccessControllerInterface.sol";
import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol";
import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol";
import {IVerifierFeeManager} from "./dev/interfaces/IVerifierFeeManager.sol";
import {Common} from "../libraries/Common.sol";

/**
Expand Down Expand Up @@ -65,6 +65,10 @@ contract VerifierProxy is IVerifierProxy, ConfirmedOwner, TypeAndVersionInterfac
/// not conform to the verifier interface
error VerifierInvalid();

/// @notice This error is thrown when the fee manager at an address does
/// not conform to the fee manager interface
error FeeManagerInvalid();

/// @notice This error is thrown whenever a verifier is not found
/// @param configDigest The digest for which a verifier is not found
error VerifierNotFound(bytes32 configDigest);
Expand Down Expand Up @@ -117,7 +121,7 @@ contract VerifierProxy is IVerifierProxy, ConfirmedOwner, TypeAndVersionInterfac
}

/// @inheritdoc IVerifierProxy
function verify(bytes calldata payload) external payable checkAccess returns (bytes memory verifiedReport) {
function verify(bytes calldata payload) external payable checkAccess returns (bytes memory) {
IVerifierFeeManager feeManager = s_feeManager;

// Bill the verifier
Expand Down Expand Up @@ -192,7 +196,7 @@ contract VerifierProxy is IVerifierProxy, ConfirmedOwner, TypeAndVersionInterfac
}

/// @inheritdoc IVerifierProxy
function getVerifier(bytes32 configDigest) external view override returns (address verifierAddress) {
function getVerifier(bytes32 configDigest) external view override returns (address) {
return s_verifiersByConfig[configDigest];
}

Expand All @@ -207,6 +211,11 @@ contract VerifierProxy is IVerifierProxy, ConfirmedOwner, TypeAndVersionInterfac
function setFeeManager(IVerifierFeeManager feeManager) external onlyOwner {
if (address(feeManager) == address(0)) revert ZeroAddress();

if (
!IERC165(feeManager).supportsInterface(IVerifierFeeManager.processFee.selector) ||
!IERC165(feeManager).supportsInterface(IVerifierFeeManager.processFeeBulk.selector)
) revert FeeManagerInvalid();

address oldFeeManager = address(s_feeManager);
s_feeManager = IVerifierFeeManager(feeManager);
emit FeeManagerSet(oldFeeManager, address(feeManager));
Expand Down
90 changes: 69 additions & 21 deletions contracts/src/v0.8/llo-feeds/dev/FeeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {IWERC20} from "../../shared/interfaces/IWERC20.sol";
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC20.sol";
import {Math} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/Math.sol";
import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol";
import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol";

/**
* @title FeeManager
Expand Down Expand Up @@ -75,6 +76,9 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
// @notice thrown when trying to clear a zero deficit
error ZeroDeficit();

/// @notice thrown when trying to pay an address that cannot except funds
error InvalidReceivingAddress();

/// @notice Emitted whenever a subscriber's discount is updated
/// @param subscriber address of the subscriber to update discounts for
/// @param feedId Feed ID for the discount
Expand All @@ -91,15 +95,31 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
event InsufficientLink(IRewardManager.FeePayment[] rewards);

/// @notice Emitted when funds are withdrawn
/// @param adminAddress Address of the admin
/// @param recipient Address of the recipient
/// @param assetAddress Address of the asset withdrawn
/// @param quantity Amount of the asset withdrawn
event Withdraw(address adminAddress, address assetAddress, uint192 quantity);
event Withdraw(address adminAddress, address recipient, address assetAddress, uint192 quantity);

/// @notice Emits when a deficit has been cleared for a particular config digest
/// @param configDigest Config digest of the deficit cleared
/// @param linkQuantity Amount of LINK required to pay the deficit
event LinkDeficitCleared(bytes32 indexed configDigest, uint256 linkQuantity);

/// @notice Emits when a fee has been processed
/// @param configDigest Config digest of the fee processed
/// @param subscriber Address of the subscriber who paid the fee
/// @param fee Fee paid
/// @param reward Reward paid
/// @param appliedDiscount Discount applied to the fee
event DiscountApplied(
bytes32 indexed configDigest,
address indexed subscriber,
Common.Asset fee,
Common.Asset reward,
uint256 appliedDiscount
);

/**
* @notice Construct the FeeManager contract
* @param _linkAddress The address of the LINK token
Expand Down Expand Up @@ -133,27 +153,32 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
_;
}

modifier onlyProxy() {
if (msg.sender != i_proxyAddress) revert Unauthorized();
_;
}

/// @inheritdoc TypeAndVersionInterface
function typeAndVersion() external pure override returns (string memory) {
return "FeeManager 0.0.1";
return "FeeManager 1.0.0";
}

/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
return interfaceId == this.processFee.selector || interfaceId == this.processFeeBulk.selector;
}

/// @inheritdoc IFeeManager
function processFee(bytes calldata payload, address subscriber) external payable override onlyOwnerOrProxy {
(Common.Asset memory fee, Common.Asset memory reward) = _processFee(payload, subscriber);
/// @inheritdoc IVerifierFeeManager
function processFee(bytes calldata payload, address subscriber) external payable override onlyProxy {
(Common.Asset memory fee, Common.Asset memory reward, uint256 appliedDiscount) = _processFee(payload, subscriber);

if (fee.amount == 0) {
_tryReturnChange(subscriber, msg.value);
return;
}

IFeeManager.FeeAndReward[] memory feeAndReward = new IFeeManager.FeeAndReward[](1);
feeAndReward[0] = IFeeManager.FeeAndReward(bytes32(payload), fee, reward);
feeAndReward[0] = IFeeManager.FeeAndReward(bytes32(payload), fee, reward, appliedDiscount);

if (fee.assetAddress == i_linkAddress) {
_handleFeesAndRewards(subscriber, feeAndReward, 1, 0);
Expand All @@ -162,8 +187,8 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
}
}

/// @inheritdoc IFeeManager
function processFeeBulk(bytes[] calldata payloads, address subscriber) external payable override onlyOwnerOrProxy {
/// @inheritdoc IVerifierFeeManager
function processFeeBulk(bytes[] calldata payloads, address subscriber) external payable override onlyProxy {
FeeAndReward[] memory feesAndRewards = new IFeeManager.FeeAndReward[](payloads.length);

//keep track of the number of fees to prevent over initialising the FeePayment array within _convertToLinkAndNativeFees
Expand All @@ -172,10 +197,18 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {

uint256 feesAndRewardsIndex;
for (uint256 i; i < payloads.length; ++i) {
(Common.Asset memory fee, Common.Asset memory reward) = _processFee(payloads[i], subscriber);
(Common.Asset memory fee, Common.Asset memory reward, uint256 appliedDiscount) = _processFee(
payloads[i],
subscriber
);

if (fee.amount != 0) {
feesAndRewards[feesAndRewardsIndex++] = IFeeManager.FeeAndReward(bytes32(payloads[i]), fee, reward);
feesAndRewards[feesAndRewardsIndex++] = IFeeManager.FeeAndReward(
bytes32(payloads[i]),
fee,
reward,
appliedDiscount
);

unchecked {
//keep track of some tallys to make downstream calculations more efficient
Expand All @@ -200,7 +233,7 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
address subscriber,
bytes memory report,
Quote memory quote
) public view returns (Common.Asset memory, Common.Asset memory) {
) public view returns (Common.Asset memory, Common.Asset memory, uint256) {
Common.Asset memory fee;
Common.Asset memory reward;

Expand All @@ -214,7 +247,7 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
if (reportVersion == REPORT_V1) {
fee.assetAddress = i_nativeAddress;
reward.assetAddress = i_linkAddress;
return (fee, reward);
return (fee, reward, 0);
}

//verify the quote payload is a supported token
Expand Down Expand Up @@ -255,10 +288,10 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
}

//return the fee
return (fee, reward);
return (fee, reward, discount);
}

/// @inheritdoc IFeeManager
/// @inheritdoc IVerifierFeeManager
function setFeeRecipients(
bytes32 configDigest,
Common.AddressAndWeight[] calldata rewardRecipientAndWeights
Expand Down Expand Up @@ -293,18 +326,20 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
}

/// @inheritdoc IFeeManager
function withdraw(address assetAddress, uint192 quantity) external onlyOwner {
function withdraw(address assetAddress, address recipient, uint192 quantity) external onlyOwner {
//address 0 is used to withdraw native in the context of withdrawing
if (assetAddress == address(0)) {
payable(owner()).transfer(quantity);
(bool success, ) = payable(recipient).call{value: quantity}("");

if (!success) revert InvalidReceivingAddress();
return;
}

//withdraw the requested asset
IERC20(assetAddress).safeTransfer(owner(), quantity);
IERC20(assetAddress).safeTransfer(recipient, quantity);

//emit event when funds are withdrawn
emit Withdraw(msg.sender, assetAddress, quantity);
emit Withdraw(msg.sender, recipient, assetAddress, uint192(quantity));
}

/// @inheritdoc IFeeManager
Expand All @@ -324,7 +359,7 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
function _processFee(
bytes calldata payload,
address subscriber
) internal view returns (Common.Asset memory, Common.Asset memory) {
) internal view returns (Common.Asset memory, Common.Asset memory, uint256) {
if (subscriber == address(this)) revert InvalidAddress();

//decode the report from the payload
Expand Down Expand Up @@ -381,6 +416,16 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
totalNativeFee += feesAndRewards[i].fee.amount;
totalNativeFeeLinkValue += feesAndRewards[i].reward.amount;
}

if (feesAndRewards[i].appliedDiscount != 0) {
emit DiscountApplied(
feesAndRewards[i].configDigest,
subscriber,
feesAndRewards[i].fee,
feesAndRewards[i].reward,
feesAndRewards[i].appliedDiscount
);
}
}

//keep track of change in case of any over payment
Expand All @@ -390,7 +435,7 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
//there must be enough to cover the fee
if (totalNativeFee > msg.value) revert InvalidDeposit();

//wrap the amount required to pay the fee & approve as the subscriber paid in unwrapped native
//wrap the amount required to pay the fee & approve as the subscriber paid in wrapped native
IWERC20(i_nativeAddress).deposit{value: totalNativeFee}();

unchecked {
Expand All @@ -413,7 +458,10 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
if (totalNativeFeeLinkValue > IERC20(i_linkAddress).balanceOf(address(this))) {
// If not enough LINK on this contract to forward for rewards, tally the deficit to be paid by out-of-band LINK
for (uint256 i; i < nativeFeeLinkRewards.length; ++i) {
s_linkDeficit[nativeFeeLinkRewards[i].poolId] += nativeFeeLinkRewards[i].amount;
unchecked {
//we have previously tallied the fees, any overflows would have already reverted
s_linkDeficit[nativeFeeLinkRewards[i].poolId] += nativeFeeLinkRewards[i].amount;
}
}

emit InsufficientLink(nativeFeeLinkRewards);
Expand Down
33 changes: 25 additions & 8 deletions contracts/src/v0.8/llo-feeds/dev/RewardManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,14 @@ contract RewardManager is IRewardManager, ConfirmedOwner, TypeAndVersionInterfac
// @notice Thrown when the calling contract is not within the authorized contracts
error Unauthorized();

// @notice Thrown when getAvailableRewardPoolIds parameters are incorrectly set
error InvalidPoolLength();

// Events emitted upon state change
event RewardRecipientsUpdated(bytes32 indexed poolId, Common.AddressAndWeight[] newRewardRecipients);
event RewardsClaimed(bytes32 indexed poolId, address indexed recipient, uint192 quantity);
event FeeManagerUpdated(address newFeeManagerAddress);
event FeePaid(FeePayment[] payments, address payee);
event FeePaid(FeePayment[] payments, address payer);

/**
* @notice Constructor
Expand All @@ -73,7 +76,7 @@ contract RewardManager is IRewardManager, ConfirmedOwner, TypeAndVersionInterfac

// @inheritdoc TypeAndVersionInterface
function typeAndVersion() external pure override returns (string memory) {
return "RewardManager 0.0.1";
return "RewardManager 1.0.0";
}

// @inheritdoc IERC165
Expand All @@ -91,8 +94,13 @@ contract RewardManager is IRewardManager, ConfirmedOwner, TypeAndVersionInterfac
_;
}

modifier onlyFeeManager() {
if (msg.sender != s_feeManagerAddress) revert Unauthorized();
_;
}

/// @inheritdoc IRewardManager
function onFeePaid(FeePayment[] calldata payments, address payee) external override onlyOwnerOrFeeManager {
function onFeePaid(FeePayment[] calldata payments, address payer) external override onlyFeeManager {
uint256 totalFeeAmount;
for (uint256 i; i < payments.length; ++i) {
unchecked {
Expand All @@ -105,9 +113,9 @@ contract RewardManager is IRewardManager, ConfirmedOwner, TypeAndVersionInterfac
}

//transfer the fees to this contract
IERC20(i_linkAddress).safeTransferFrom(payee, address(this), totalFeeAmount);
IERC20(i_linkAddress).safeTransferFrom(payer, address(this), totalFeeAmount);

emit FeePaid(payments, payee);
emit FeePaid(payments, payer);
}

/// @inheritdoc IRewardManager
Expand Down Expand Up @@ -276,19 +284,28 @@ contract RewardManager is IRewardManager, ConfirmedOwner, TypeAndVersionInterfac
}

/// @inheritdoc IRewardManager
function getAvailableRewardPoolIds(address recipient) external view returns (bytes32[] memory) {
function getAvailableRewardPoolIds(
address recipient,
uint256 startIndex,
uint256 endIndex
) external view returns (bytes32[] memory) {
//get the length of the pool ids which we will loop through and potentially return
uint256 registeredPoolIdsLength = s_registeredPoolIds.length;

uint256 lastIndex = endIndex > registeredPoolIdsLength ? registeredPoolIdsLength : endIndex;

if (startIndex > lastIndex) revert InvalidPoolLength();

//create a new array with the maximum amount of potential pool ids
bytes32[] memory claimablePoolIds = new bytes32[](registeredPoolIdsLength);
bytes32[] memory claimablePoolIds = new bytes32[](lastIndex - startIndex);
//we want the pools which a recipient has funds for to be sequential, so we need to keep track of the index
uint256 poolIdArrayIndex;

//loop all the pool ids, and check if the recipient has a registered weight and a claimable amount
for (uint256 i; i < registeredPoolIdsLength; ++i) {
for (uint256 i = startIndex; i < lastIndex; ++i) {
//get the poolId
bytes32 poolId = s_registeredPoolIds[i];

//if the recipient has a weight, they are a recipient of this poolId
if (s_rewardRecipientWeights[poolId][recipient] != 0) {
//get the total in this pool
Expand Down
Loading

0 comments on commit 2ff674b

Please sign in to comment.