Skip to content

Commit

Permalink
Verify with single payment token (#10603)
Browse files Browse the repository at this point in the history
* Flip fee tokens in report decoding

* Use array of quotePayload

* Update interface to single quote payload

* Passing tests

* Gas snapshot update

* Update go gen files

* Update integration test interfaces

* Prettify

* Update snapshot

* Change second parameter name to parameterPayload

* Prettier

* feePayload -> parameterPayload in integration tests
  • Loading branch information
austinborn authored Sep 19, 2023
1 parent df9c499 commit af1f6e7
Show file tree
Hide file tree
Showing 23 changed files with 497 additions and 691 deletions.
263 changes: 129 additions & 134 deletions contracts/gas-snapshots/llo-feeds.gas-snapshot

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions contracts/src/v0.8/llo-feeds/VerifierProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,24 +121,30 @@ contract VerifierProxy is IVerifierProxy, ConfirmedOwner, TypeAndVersionInterfac
}

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

// Bill the verifier
if (address(feeManager) != address(0)) {
feeManager.processFee{value: msg.value}(payload, msg.sender);
feeManager.processFee{value: msg.value}(payload, parameterPayload, msg.sender);
}

return _verify(payload);
}

/// @inheritdoc IVerifierProxy
function verifyBulk(bytes[] calldata payloads) external payable checkAccess returns (bytes[] memory verifiedReports) {
function verifyBulk(
bytes[] calldata payloads,
bytes calldata parameterPayload
) external payable checkAccess returns (bytes[] memory verifiedReports) {
IVerifierFeeManager feeManager = s_feeManager;

// Bill the verifier
if (address(feeManager) != address(0)) {
feeManager.processFeeBulk{value: msg.value}(payloads, msg.sender);
feeManager.processFeeBulk{value: msg.value}(payloads, parameterPayload, msg.sender);
}

//verify the reports
Expand Down
39 changes: 23 additions & 16 deletions contracts/src/v0.8/llo-feeds/dev/FeeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,16 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
}

/// @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);
function processFee(
bytes calldata payload,
bytes calldata parameterPayload,
address subscriber
) external payable override onlyProxy {
(Common.Asset memory fee, Common.Asset memory reward, uint256 appliedDiscount) = _processFee(
payload,
parameterPayload,
subscriber
);

if (fee.amount == 0) {
_tryReturnChange(subscriber, msg.value);
Expand All @@ -188,7 +196,11 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
}

/// @inheritdoc IVerifierFeeManager
function processFeeBulk(bytes[] calldata payloads, address subscriber) external payable override onlyProxy {
function processFeeBulk(
bytes[] calldata payloads,
bytes calldata parameterPayload,
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 @@ -199,6 +211,7 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
for (uint256 i; i < payloads.length; ++i) {
(Common.Asset memory fee, Common.Asset memory reward, uint256 appliedDiscount) = _processFee(
payloads[i],
parameterPayload,
subscriber
);

Expand Down Expand Up @@ -232,7 +245,7 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
function getFeeAndReward(
address subscriber,
bytes memory report,
Quote memory quote
address quoteAddress
) public view returns (Common.Asset memory, Common.Asset memory, uint256) {
Common.Asset memory fee;
Common.Asset memory reward;
Expand All @@ -251,7 +264,7 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
}

//verify the quote payload is a supported token
if (quote.quoteAddress != i_nativeAddress && quote.quoteAddress != i_linkAddress) {
if (quoteAddress != i_nativeAddress && quoteAddress != i_linkAddress) {
revert InvalidQuote();
}

Expand All @@ -270,14 +283,14 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
}

//get the discount being applied
uint256 discount = s_subscriberDiscounts[subscriber][feedId][quote.quoteAddress];
uint256 discount = s_subscriberDiscounts[subscriber][feedId][quoteAddress];

//the reward is always set in LINK
reward.assetAddress = i_linkAddress;
reward.amount = Math.ceilDiv(linkQuantity * (PERCENTAGE_SCALAR - discount), PERCENTAGE_SCALAR);

//calculate either the LINK fee or native fee if it's within the report
if (quote.quoteAddress == i_linkAddress) {
if (quoteAddress == i_linkAddress) {
fee.assetAddress = i_linkAddress;
fee.amount = reward.amount;
} else {
Expand Down Expand Up @@ -358,6 +371,7 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {

function _processFee(
bytes calldata payload,
bytes calldata parameterPayload,
address subscriber
) internal view returns (Common.Asset memory, Common.Asset memory, uint256) {
if (subscriber == address(this)) revert InvalidAddress();
Expand All @@ -369,17 +383,10 @@ contract FeeManager is IFeeManager, ConfirmedOwner, TypeAndVersionInterface {
bytes32 feedId = bytes32(report);

//v1 doesn't need a quote payload, so skip the decoding
Quote memory quote;
address quote;
if (_getReportVersion(feedId) != REPORT_V1) {
//all reports greater than v1 should have a quote payload
(, , , , , bytes memory quoteBytes) = abi.decode(
payload,
// reportContext, report, rs, ss, raw, quote
(bytes32[3], bytes, bytes32[], bytes32[], bytes32, bytes)
);

//decode the quote from the bytes
(quote) = abi.decode(quoteBytes, (Quote));
(quote) = abi.decode(parameterPayload, (address));
}

//decode the fee, it will always be native or LINK
Expand Down
4 changes: 2 additions & 2 deletions contracts/src/v0.8/llo-feeds/dev/interfaces/IFeeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ interface IFeeManager is IERC165, IVerifierFeeManager {
* @notice Calculate the applied fee and the reward from a report. If the sender is a subscriber, they will receive a discount.
* @param subscriber address trying to verify
* @param report report to calculate the fee for
* @param quote any metadata required to fetch the fee
* @param quoteAddress address of the quote payment token
* @return (fee, reward, totalDiscount) fee and the reward data with the discount applied
*/
function getFeeAndReward(
address subscriber,
bytes memory report,
Quote memory quote
address quoteAddress
) external returns (Common.Asset memory, Common.Asset memory, uint256);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@ import {Common} from "../../../libraries/Common.sol";
interface IVerifierFeeManager is IERC165 {
/**
* @notice Handles fees for a report from the subscriber and manages rewards
* @param payload report and quote to process the fee for
* @param payload report to process the fee for
* @param parameterPayload fee payload
* @param subscriber address of the fee will be applied
*/
function processFee(bytes calldata payload, address subscriber) external payable;
function processFee(bytes calldata payload, bytes calldata parameterPayload, address subscriber) external payable;

/**
* @notice Processes the fees for each report in the payload, billing the subscriber and paying the reward manager
* @param payloads reports and quotes to process
* @param payloads reports to process
* @param parameterPayload fee payload
* @param subscriber address of the user to process fee for
*/
function processFeeBulk(bytes[] calldata payloads, address subscriber) external payable;
function processFeeBulk(
bytes[] calldata payloads,
bytes calldata parameterPayload,
address subscriber
) external payable;

/**
* @notice Sets the fee recipients according to the fee manager
Expand Down
16 changes: 12 additions & 4 deletions contracts/src/v0.8/llo-feeds/interfaces/IVerifierProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,27 @@ interface IVerifierProxy {
* @notice Verifies that the data encoded has been signed
* correctly by routing to the correct verifier, and bills the user if applicable.
* @param payload The encoded data to be verified, including the signed
* report and any metadata for billing.
* report.
* @param parameterPayload fee metadata for billing
* @return verifierResponse The encoded report from the verifier.
*/
function verify(bytes calldata payload) external payable returns (bytes memory verifierResponse);
function verify(
bytes calldata payload,
bytes calldata parameterPayload
) external payable returns (bytes memory verifierResponse);

/**
* @notice Bulk verifies that the data encoded has been signed
* correctly by routing to the correct verifier, and bills the user if applicable.
* @param payloads The encoded payloads to be verified, including the signed
* report and any metadata for billing.
* report.
* @param parameterPayload fee metadata for billing
* @return verifiedReports The encoded reports from the verifier.
*/
function verifyBulk(bytes[] calldata payloads) external payable returns (bytes[] memory verifiedReports);
function verifyBulk(
bytes[] calldata payloads,
bytes calldata parameterPayload
) external payable returns (bytes[] memory verifiedReports);

/**
* @notice Sets the verifier address initially, allowing `setVerifier` to be set by this Verifier in the future
Expand Down
70 changes: 22 additions & 48 deletions contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ contract BaseFeeManagerTest is Test {
native = new WERC20Mock();

feeManagerProxy = new FeeManagerProxy();
rewardManager = new RewardManager(getLinkAddress());
feeManager = new FeeManager(getLinkAddress(), getNativeAddress(), address(feeManagerProxy), address(rewardManager));
rewardManager = new RewardManager(address(link));
feeManager = new FeeManager(address(link), address(native), address(feeManagerProxy), address(rewardManager));

//link the feeManager to the proxy
feeManagerProxy.setFeeManager(feeManager);
Expand Down Expand Up @@ -154,33 +154,21 @@ contract BaseFeeManagerTest is Test {
}

// solium-disable-next-line no-unused-vars
function getFee(
bytes memory report,
IFeeManager.Quote memory quote,
address subscriber
) public view returns (Common.Asset memory) {
function getFee(bytes memory report, address quote, address subscriber) public view returns (Common.Asset memory) {
//get the fee
(Common.Asset memory fee, , ) = feeManager.getFeeAndReward(subscriber, report, quote);

return fee;
}

function getReward(
bytes memory report,
IFeeManager.Quote memory quote,
address subscriber
) public view returns (Common.Asset memory) {
function getReward(bytes memory report, address quote, address subscriber) public view returns (Common.Asset memory) {
//get the reward
(, Common.Asset memory reward, ) = feeManager.getFeeAndReward(subscriber, report, quote);

return reward;
}

function getAppliedDiscount(
bytes memory report,
IFeeManager.Quote memory quote,
address subscriber
) public view returns (uint256) {
function getAppliedDiscount(bytes memory report, address quote, address subscriber) public view returns (uint256) {
//get the reward
(, , uint256 appliedDiscount) = feeManager.getFeeAndReward(subscriber, report, quote);

Expand Down Expand Up @@ -239,12 +227,12 @@ contract BaseFeeManagerTest is Test {
);
}

function getLinkQuote() public view returns (IFeeManager.Quote memory) {
return IFeeManager.Quote(getLinkAddress());
function getLinkQuote() public view returns (address) {
return address(link);
}

function getNativeQuote() public view returns (IFeeManager.Quote memory) {
return IFeeManager.Quote(getNativeAddress());
function getNativeQuote() public view returns (address) {
return address(native);
}

function withdraw(address assetAddress, address recipient, uint256 amount, address sender) public {
Expand Down Expand Up @@ -302,6 +290,7 @@ contract BaseFeeManagerTest is Test {
function ProcessFeeAsUser(
bytes memory payload,
address subscriber,
address tokenAddress,
uint256 wrappedNativeValue,
address sender
) public {
Expand All @@ -310,50 +299,43 @@ contract BaseFeeManagerTest is Test {
changePrank(sender);

//process the fee
feeManager.processFee{value: wrappedNativeValue}(payload, subscriber);
feeManager.processFee{value: wrappedNativeValue}(payload, abi.encode(tokenAddress), subscriber);

//change ProcessFeeAsUserback to the original address
changePrank(originalAddr);
}

function processFee(bytes memory payload, address subscriber, uint256 wrappedNativeValue) public {
function processFee(bytes memory payload, address subscriber, address feeAddress, uint256 wrappedNativeValue) public {
//record the current address and switch to the recipient
address originalAddr = msg.sender;
changePrank(subscriber);

//process the fee
feeManagerProxy.processFee{value: wrappedNativeValue}(payload);
feeManagerProxy.processFee{value: wrappedNativeValue}(payload, abi.encode(feeAddress));

//change back to the original address
changePrank(originalAddr);
}

function processFee(bytes[] memory payloads, address subscriber, uint256 wrappedNativeValue) public {
function processFee(
bytes[] memory payloads,
address subscriber,
address feeAddress,
uint256 wrappedNativeValue
) public {
//record the current address and switch to the recipient
address originalAddr = msg.sender;
changePrank(subscriber);

//process the fee
feeManagerProxy.processFeeBulk{value: wrappedNativeValue}(payloads);
feeManagerProxy.processFeeBulk{value: wrappedNativeValue}(payloads, abi.encode(feeAddress));

//change back to the original address
changePrank(originalAddr);
}

function getPayload(bytes memory reportPayload, bytes memory quotePayload) public pure returns (bytes memory) {
return
abi.encode(
[DEFAULT_CONFIG_DIGEST, 0, 0],
reportPayload,
new bytes32[](1),
new bytes32[](1),
bytes32(""),
quotePayload
);
}

function getQuotePayload(address quoteAddress) public pure returns (bytes memory) {
return abi.encode(quoteAddress);
function getPayload(bytes memory reportPayload) public pure returns (bytes memory) {
return abi.encode([DEFAULT_CONFIG_DIGEST, 0, 0], reportPayload, new bytes32[](1), new bytes32[](1), bytes32(""));
}

function approveLink(address spender, uint256 quantity, address sender) public {
Expand All @@ -380,14 +362,6 @@ contract BaseFeeManagerTest is Test {
changePrank(originalAddr);
}

function getLinkAddress() public view returns (address) {
return address(link);
}

function getNativeAddress() public view returns (address) {
return address(native);
}

function payLinkDeficit(bytes32 configDigest, address sender) public {
//record the current address and switch to the recipient
address originalAddr = msg.sender;
Expand Down
Loading

0 comments on commit af1f6e7

Please sign in to comment.