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,38 +4,44 @@ pragma solidity 0.8.9;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

/**
* @title CustomAggregatorV3CompatibleFeedDiscounted
* @notice AggregatorV3 compatible proxy-feed that discounts the price
* of an underlying chainlink compatible feed by a given percentage
* @title CustomAggregatorV3CompatibleFeedAdjusted
* @notice AggregatorV3 compatible proxy-feed that adjusts the price
* of an underlying chainlink compatible feed by a given signed percentage.
* Positive adjustmentPercentage raises the reported price.
* Negative adjustmentPercentage lowers the reported price.
* @author RedDuck Software
*/
contract CustomAggregatorV3CompatibleFeedDiscounted is AggregatorV3Interface {
contract CustomAggregatorV3CompatibleFeedAdjusted is AggregatorV3Interface {
/**
* @notice the underlying chainlink compatible feed
*/
AggregatorV3Interface public immutable underlyingFeed;

/**
* @notice the discount percentage. Expressed in 10 ** decimals() precision
* Example: 10 ** decimals() = 1%
* @notice the adjustment percentage (signed).
* Expressed in 10 ** decimals() precision.
* Example: 10 ** decimals() = 1%, -(10 ** decimals()) = -1%
* Positive values raise the reported price.
* Negative values lower the reported price.
*/
uint256 public immutable discountPercentage;
int256 public immutable adjustmentPercentage;

/**
* @notice constructor
* @param _underlyingFeed the underlying chainlink compatible feed
* @param _discountPercentage the discount percentage. Expressed in 10 ** decimals() precision
* @param _adjustmentPercentage signed adjustment percentage in 10 ** decimals() precision
*/
constructor(address _underlyingFeed, uint256 _discountPercentage) {
require(_underlyingFeed != address(0), "CAD: !underlying feed");
constructor(address _underlyingFeed, int256 _adjustmentPercentage) {
require(_underlyingFeed != address(0), "CAA: !underlying feed");
underlyingFeed = AggregatorV3Interface(_underlyingFeed);

int256 maxPct = int256(100 * (10**decimals()));
require(
_discountPercentage <= 100 * (10**decimals()),
"CAD: !discount percentage"
_adjustmentPercentage >= -maxPct && _adjustmentPercentage <= maxPct,
"CAA: invalid adjustment"
);

discountPercentage = _discountPercentage;
adjustmentPercentage = _adjustmentPercentage;
}

/**
Expand All @@ -60,7 +66,7 @@ contract CustomAggregatorV3CompatibleFeedDiscounted is AggregatorV3Interface {
answeredInRound
) = underlyingFeed.latestRoundData();

answer = _calculateDiscountedAnswer(answer);
answer = _calculateAdjustedAnswer(answer);
}

/**
Expand Down Expand Up @@ -91,7 +97,7 @@ contract CustomAggregatorV3CompatibleFeedDiscounted is AggregatorV3Interface {
updatedAt,
answeredInRound
) = underlyingFeed.getRoundData(_roundId);
answer = _calculateDiscountedAnswer(answer);
answer = _calculateAdjustedAnswer(answer);
}

/**
Expand All @@ -105,27 +111,28 @@ contract CustomAggregatorV3CompatibleFeedDiscounted is AggregatorV3Interface {
* @inheritdoc AggregatorV3Interface
*/
function description() public view returns (string memory) {
return
string(
abi.encodePacked(underlyingFeed.description(), " Discounted")
);
if (adjustmentPercentage == 0) return underlyingFeed.description();
string memory suffix = adjustmentPercentage > 0
? " PriceRaised"
: " PriceLowered";
return string(abi.encodePacked(underlyingFeed.description(), suffix));
}

/**
* @dev calculates the discounted answer
* @param _answer the answer to discount
* @return the discounted answer
* @dev calculates the adjusted answer
* @param _answer the answer to adjust
* @return the adjusted answer
*/
function _calculateDiscountedAnswer(int256 _answer)
function _calculateAdjustedAnswer(int256 _answer)
internal
view
returns (int256)
{
require(_answer >= 0, "CAD: !_answer");
require(_answer >= 0, "CAA: !_answer");

int256 discount = (_answer * int256(discountPercentage)) /
int256 adjustment = (_answer * adjustmentPercentage) /
int256(100 * 10**decimals());
Comment on lines +133 to 134
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The calculation of the adjustment denominator involves an external call to decimals() on every execution of latestRoundData and getRoundData. Since the underlyingFeed is immutable and its decimals are constant for a given feed, this value should be cached in an immutable variable during construction to save gas.


return _answer - discount;
return _answer + adjustment;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

import "../feeds/CustomAggregatorV3CompatibleFeedAdjusted.sol";

contract CustomAggregatorV3CompatibleFeedAdjustedTester is
CustomAggregatorV3CompatibleFeedAdjusted
{
constructor(address _underlyingFeed, int256 _adjustmentPercentage)
CustomAggregatorV3CompatibleFeedAdjusted(
_underlyingFeed,
_adjustmentPercentage
)
{}

function getAdjustedAnswer(int256 _answer) public view returns (int256) {
return _calculateAdjustedAnswer(_answer);
}
}

This file was deleted.

4 changes: 2 additions & 2 deletions helpers/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export type TokenContractNames = {
type CommonContractNames = Omit<TokenContractNames, 'token'> & {
ac: string;
customAggregator: string;
customAggregatorDiscounted: string;
customAggregatorAdjusted: string;
layerZero: {
oftAdapter: string;
vaultComposer: string;
Expand Down Expand Up @@ -149,7 +149,7 @@ export const getCommonContractNames = (): CommonContractNames => {
dataFeed: 'DataFeed',
customAggregator: 'CustomAggregatorV3CompatibleFeed',
customAggregatorGrowth: 'CustomAggregatorV3CompatibleFeedGrowth',
customAggregatorDiscounted: 'CustomAggregatorV3CompatibleFeedDiscounted',
customAggregatorAdjusted: 'CustomAggregatorV3CompatibleFeedAdjusted',
roles: 'MidasAccessControlRoles',
dataFeedComposite: 'CompositeDataFeed',
dataFeedMultiply: 'CompositeDataFeedMultiply',
Expand Down
18 changes: 9 additions & 9 deletions scripts/deploy/common/data-feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export type DeployCustomAggregatorRegularConfig =
type?: 'REGULAR';
};

export type DeployCustomAggregatorDiscountedConfig = {
discountPercentage: BigNumberish;
export type DeployCustomAggregatorAdjustedConfig = {
adjustmentPercentage: BigNumberish;
underlyingFeed: `0x${string}` | 'customFeed';
};

Expand Down Expand Up @@ -410,14 +410,14 @@ export const deployMTokenDataFeed = async (
);
};

export const deployMTokenCustomAggregatorDiscounted = async (
export const deployMTokenCustomAggregatorAdjusted = async (
hre: HardhatRuntimeEnvironment,
token: MTokenName,
) => {
await deployCustomAggregatorDiscounted(
await deployCustomAggregatorAdjusted(
hre,
token,
getDeploymentGenericConfig(hre, token, 'customAggregatorDiscounted'),
getDeploymentGenericConfig(hre, token, 'customAggregatorAdjusted'),
);
};

Expand Down Expand Up @@ -536,10 +536,10 @@ const deployCustomAggregator = async (
);
};

const deployCustomAggregatorDiscounted = async (
const deployCustomAggregatorAdjusted = async (
hre: HardhatRuntimeEnvironment,
token: MTokenName,
networkConfig?: DeployCustomAggregatorDiscountedConfig,
networkConfig?: DeployCustomAggregatorAdjustedConfig,
) => {
const addresses = getCurrentAddresses(hre);

Expand All @@ -558,7 +558,7 @@ const deployCustomAggregatorDiscounted = async (

await deployAndVerify(
hre,
getCommonContractNames().customAggregatorDiscounted,
[underlyingFeed, networkConfig.discountPercentage],
getCommonContractNames().customAggregatorAdjusted,
[underlyingFeed, networkConfig.adjustmentPercentage],
);
};
4 changes: 2 additions & 2 deletions scripts/deploy/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types';

import { AddFeeWaivedConfig, AddPaymentTokensConfig } from './common-vault';
import {
DeployCustomAggregatorAdjustedConfig,
DeployCustomAggregatorConfig,
DeployCustomAggregatorDiscountedConfig,
DeployDataFeedConfig,
SetRoundDataConfig,
} from './data-feed';
Expand Down Expand Up @@ -94,7 +94,7 @@ export type PostDeployConfig = {
export type DeploymentConfig = {
genericConfigs: {
customAggregator?: DeployCustomAggregatorConfig;
customAggregatorDiscounted?: DeployCustomAggregatorDiscountedConfig;
customAggregatorAdjusted?: DeployCustomAggregatorAdjustedConfig;
dataFeed?: DeployDataFeedConfig;
};
networkConfigs: Record<
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { HardhatRuntimeEnvironment } from 'hardhat/types';

import { getMTokenOrThrow } from '../../../helpers/utils';
import { deployMTokenCustomAggregatorDiscounted } from '../common/data-feed';
import { deployMTokenCustomAggregatorAdjusted } from '../common/data-feed';
import { DeployFunction } from '../common/types';

const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const mToken = getMTokenOrThrow(hre);
await deployMTokenCustomAggregatorDiscounted(hre, mToken);
await deployMTokenCustomAggregatorAdjusted(hre, mToken);
};

export default func;
8 changes: 4 additions & 4 deletions test/common/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {
RedemptionVaultWithMTokenTest__factory,
AaveV3PoolMock__factory,
MorphoVaultMock__factory,
CustomAggregatorV3CompatibleFeedDiscountedTester__factory,
CustomAggregatorV3CompatibleFeedAdjustedTester__factory,
DepositVaultWithAaveTest__factory,
DepositVaultWithMorphoTest__factory,
DepositVaultWithMTokenTest__factory,
Expand Down Expand Up @@ -714,8 +714,8 @@ export const defaultDeploy = async () => {
parseUnits('10000', mockedAggregatorDecimals),
);

const customFeedDiscounted =
await new CustomAggregatorV3CompatibleFeedDiscountedTester__factory(
const customFeedAdjusted =
await new CustomAggregatorV3CompatibleFeedAdjustedTester__factory(
owner,
).deploy(customFeed.address, parseUnits('10', 8));

Expand Down Expand Up @@ -820,7 +820,7 @@ export const defaultDeploy = async () => {

return {
customFeed,
customFeedDiscounted,
customFeedAdjusted,
customFeedGrowth,
mTBILL,
mBASIS,
Expand Down
Loading
Loading