Skip to content

Commit 79294d0

Browse files
committed
feat: support custom quote currencies. Added base scripts for custom atokens and rates.
1 parent ca74be8 commit 79294d0

25 files changed

+593
-314
lines changed

contracts/misc/AaveOracleV2.sol

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// SPDX-License-Identifier: agpl-3.0
2+
pragma solidity 0.6.12;
3+
4+
import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
5+
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
6+
7+
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
8+
import {IChainlinkAggregator} from '../interfaces/IChainlinkAggregator.sol';
9+
import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
10+
11+
/// @title AaveOracleV2
12+
/// @author Aave
13+
/// @notice Proxy smart contract to get the price of an asset from a price source, with Chainlink Aggregator
14+
/// smart contracts as primary option
15+
/// - If the returned price by a Chainlink aggregator is <= 0, the call is forwarded to a fallbackOracle
16+
/// - Owned by the Aave governance system, allowed to add sources for assets, replace them
17+
/// and change the fallbackOracle
18+
contract AaveOracleV2 is IPriceOracleGetter, Ownable {
19+
using SafeERC20 for IERC20;
20+
21+
event QuoteCurrencySet(address indexed quoteCurrency, uint256 quoteUnit);
22+
event AssetSourceUpdated(address indexed asset, address indexed source);
23+
event FallbackOracleUpdated(address indexed fallbackOracle);
24+
25+
mapping(address => IChainlinkAggregator) private assetsSources;
26+
IPriceOracleGetter private _fallbackOracle;
27+
address public immutable QUOTE_CURRENCY;
28+
uint256 public immutable QUOTE_CURRENCY_UNIT;
29+
30+
/// @notice Constructor
31+
/// @param assets The addresses of the assets
32+
/// @param sources The address of the source of each asset
33+
/// @param fallbackOracle The address of the fallback oracle to use if the data of an
34+
/// aggregator is not consistent
35+
constructor(
36+
address[] memory assets,
37+
address[] memory sources,
38+
address fallbackOracle,
39+
address quoteCurrency,
40+
uint256 quoteCurrencyUnits
41+
) public {
42+
_setFallbackOracle(fallbackOracle);
43+
_setAssetsSources(assets, sources);
44+
QUOTE_CURRENCY = quoteCurrency;
45+
QUOTE_CURRENCY_UNIT = quoteCurrencyUnits;
46+
emit QuoteCurrencySet(quoteCurrency, quoteCurrencyUnits);
47+
}
48+
49+
/// @notice External function called by the Aave governance to set or replace sources of assets
50+
/// @param assets The addresses of the assets
51+
/// @param sources The address of the source of each asset
52+
function setAssetSources(address[] calldata assets, address[] calldata sources)
53+
external
54+
onlyOwner
55+
{
56+
_setAssetsSources(assets, sources);
57+
}
58+
59+
/// @notice Sets the fallbackOracle
60+
/// - Callable only by the Aave governance
61+
/// @param fallbackOracle The address of the fallbackOracle
62+
function setFallbackOracle(address fallbackOracle) external onlyOwner {
63+
_setFallbackOracle(fallbackOracle);
64+
}
65+
66+
/// @notice Internal function to set the sources for each asset
67+
/// @param assets The addresses of the assets
68+
/// @param sources The address of the source of each asset
69+
function _setAssetsSources(address[] memory assets, address[] memory sources) internal {
70+
require(assets.length == sources.length, 'INCONSISTENT_PARAMS_LENGTH');
71+
for (uint256 i = 0; i < assets.length; i++) {
72+
assetsSources[assets[i]] = IChainlinkAggregator(sources[i]);
73+
emit AssetSourceUpdated(assets[i], sources[i]);
74+
}
75+
}
76+
77+
/// @notice Internal function to set the fallbackOracle
78+
/// @param fallbackOracle The address of the fallbackOracle
79+
function _setFallbackOracle(address fallbackOracle) internal {
80+
_fallbackOracle = IPriceOracleGetter(fallbackOracle);
81+
emit FallbackOracleUpdated(fallbackOracle);
82+
}
83+
84+
/// @notice Gets an asset price by address
85+
/// @param asset The asset address
86+
function getAssetPrice(address asset) public view override returns (uint256) {
87+
IChainlinkAggregator source = assetsSources[asset];
88+
89+
if (asset == QUOTE_CURRENCY) {
90+
return QUOTE_CURRENCY_UNIT;
91+
} else if (address(source) == address(0)) {
92+
return _fallbackOracle.getAssetPrice(asset);
93+
} else {
94+
int256 price = IChainlinkAggregator(source).latestAnswer();
95+
if (price > 0) {
96+
return uint256(price);
97+
} else {
98+
return _fallbackOracle.getAssetPrice(asset);
99+
}
100+
}
101+
}
102+
103+
/// @notice Gets a list of prices from a list of assets addresses
104+
/// @param assets The list of assets addresses
105+
function getAssetsPrices(address[] calldata assets) external view returns (uint256[] memory) {
106+
uint256[] memory prices = new uint256[](assets.length);
107+
for (uint256 i = 0; i < assets.length; i++) {
108+
prices[i] = getAssetPrice(assets[i]);
109+
}
110+
return prices;
111+
}
112+
113+
/// @notice Gets the address of the source for an asset address
114+
/// @param asset The address of the asset
115+
/// @return address The address of the source
116+
function getSourceOfAsset(address asset) external view returns (address) {
117+
return address(assetsSources[asset]);
118+
}
119+
120+
/// @notice Gets the address of the fallback oracle
121+
/// @return address The addres of the fallback oracle
122+
function getFallbackOracle() external view returns (address) {
123+
return address(_fallbackOracle);
124+
}
125+
}

hardhat.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ const buidlerConfig: HardhatUserConfig = {
9696
kovan: getCommonNetworkConfig(eEthereumNetwork.kovan, 42),
9797
ropsten: getCommonNetworkConfig(eEthereumNetwork.ropsten, 3),
9898
main: getCommonNetworkConfig(eEthereumNetwork.main, 1),
99-
tenderlyMain: getCommonNetworkConfig(eEthereumNetwork.tenderlyMain, 3030),
99+
tenderly: getCommonNetworkConfig(eEthereumNetwork.tenderly, 3030),
100100
matic: getCommonNetworkConfig(ePolygonNetwork.matic, 137),
101101
mumbai: getCommonNetworkConfig(ePolygonNetwork.mumbai, 80001),
102102
xdai: getCommonNetworkConfig(eXDaiNetwork.xdai, 100),

helper-hardhat-config.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const NETWORKS_RPC_URL: iParamsPerNetwork<string> = {
4545
[eEthereumNetwork.coverage]: 'http://localhost:8555',
4646
[eEthereumNetwork.hardhat]: 'http://localhost:8545',
4747
[eEthereumNetwork.buidlerevm]: 'http://localhost:8545',
48-
[eEthereumNetwork.tenderlyMain]: `https://rpc.tenderly.co/fork/${TENDERLY_FORK_ID}`,
48+
[eEthereumNetwork.tenderly]: `https://rpc.tenderly.co/fork/`,
4949
[ePolygonNetwork.mumbai]: 'https://rpc-mumbai.maticvigil.com',
5050
[ePolygonNetwork.matic]: 'https://rpc-mainnet.matic.network',
5151
[eXDaiNetwork.xdai]: 'https://rpc.xdaichain.com/',
@@ -58,7 +58,7 @@ export const NETWORKS_DEFAULT_GAS: iParamsPerNetwork<number> = {
5858
[eEthereumNetwork.coverage]: 65 * GWEI,
5959
[eEthereumNetwork.hardhat]: 65 * GWEI,
6060
[eEthereumNetwork.buidlerevm]: 65 * GWEI,
61-
[eEthereumNetwork.tenderlyMain]: 0.01 * GWEI,
61+
[eEthereumNetwork.tenderly]: 1 * GWEI,
6262
[ePolygonNetwork.mumbai]: 1 * GWEI,
6363
[ePolygonNetwork.matic]: 1 * GWEI,
6464
[eXDaiNetwork.xdai]: 1 * GWEI,
@@ -71,7 +71,7 @@ export const BLOCK_TO_FORK: iParamsPerNetwork<number | undefined> = {
7171
[eEthereumNetwork.coverage]: undefined,
7272
[eEthereumNetwork.hardhat]: undefined,
7373
[eEthereumNetwork.buidlerevm]: undefined,
74-
[eEthereumNetwork.tenderlyMain]: 12406069,
74+
[eEthereumNetwork.tenderly]: undefined,
7575
[ePolygonNetwork.mumbai]: undefined,
7676
[ePolygonNetwork.matic]: undefined,
7777
[eXDaiNetwork.xdai]: undefined,

helpers/configuration.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import {
33
iMultiPoolsAssets,
44
IReserveParams,
55
PoolConfiguration,
6-
ICommonConfiguration,
76
eNetwork,
7+
IBaseConfiguration,
88
} from './types';
99
import { getEthersSignersAddresses, getParamPerPool } from './contracts-helpers';
1010
import AaveConfig from '../markets/aave';
1111
import MaticConfig from '../markets/matic';
1212
import AmmConfig from '../markets/amm';
13+
1314
import { CommonsConfig } from '../markets/aave/commons';
1415
import { DRE, filterMapBy } from './misc-utils';
1516
import { tEthereumAddress } from './types';
@@ -59,7 +60,7 @@ export const getReservesConfigByPool = (pool: AavePools): iMultiPoolsAssets<IRes
5960
);
6061

6162
export const getGenesisPoolAdmin = async (
62-
config: ICommonConfiguration
63+
config: IBaseConfiguration
6364
): Promise<tEthereumAddress> => {
6465
const currentNetwork = process.env.FORK ? process.env.FORK : DRE.network.name;
6566
const targetAddress = getParamPerNetwork(config.PoolAdmin, <eNetwork>currentNetwork);
@@ -71,9 +72,7 @@ export const getGenesisPoolAdmin = async (
7172
return addressList[addressIndex];
7273
};
7374

74-
export const getEmergencyAdmin = async (
75-
config: ICommonConfiguration
76-
): Promise<tEthereumAddress> => {
75+
export const getEmergencyAdmin = async (config: IBaseConfiguration): Promise<tEthereumAddress> => {
7776
const currentNetwork = process.env.FORK ? process.env.FORK : DRE.network.name;
7877
const targetAddress = getParamPerNetwork(config.EmergencyAdmin, <eNetwork>currentNetwork);
7978
if (targetAddress) {
@@ -84,19 +83,17 @@ export const getEmergencyAdmin = async (
8483
return addressList[addressIndex];
8584
};
8685

87-
export const getTreasuryAddress = async (
88-
config: ICommonConfiguration
89-
): Promise<tEthereumAddress> => {
86+
export const getTreasuryAddress = async (config: IBaseConfiguration): Promise<tEthereumAddress> => {
9087
const currentNetwork = process.env.FORK ? process.env.FORK : DRE.network.name;
9188
return getParamPerNetwork(config.ReserveFactorTreasuryAddress, <eNetwork>currentNetwork);
9289
};
9390

9491
export const getATokenDomainSeparatorPerNetwork = (
9592
network: eNetwork,
96-
config: ICommonConfiguration
93+
config: IBaseConfiguration
9794
): tEthereumAddress => getParamPerNetwork<tEthereumAddress>(config.ATokenDomainSeparator, network);
9895

99-
export const getWethAddress = async (config: ICommonConfiguration) => {
96+
export const getWethAddress = async (config: IBaseConfiguration) => {
10097
const currentNetwork = process.env.FORK ? process.env.FORK : DRE.network.name;
10198
const wethAddress = getParamPerNetwork(config.WETH, <eNetwork>currentNetwork);
10299
if (wethAddress) {
@@ -109,7 +106,7 @@ export const getWethAddress = async (config: ICommonConfiguration) => {
109106
return weth.address;
110107
};
111108

112-
export const getWrappedNativeTokenAddress = async (config: ICommonConfiguration) => {
109+
export const getWrappedNativeTokenAddress = async (config: IBaseConfiguration) => {
113110
const currentNetwork = process.env.MAINNET_FORK === 'true' ? 'main' : DRE.network.name;
114111
const wethAddress = getParamPerNetwork(config.WrappedNativeToken, <eNetwork>currentNetwork);
115112
if (wethAddress) {
@@ -122,7 +119,7 @@ export const getWrappedNativeTokenAddress = async (config: ICommonConfiguration)
122119
return weth.address;
123120
};
124121

125-
export const getLendingRateOracles = (poolConfig: ICommonConfiguration) => {
122+
export const getLendingRateOracles = (poolConfig: IBaseConfiguration) => {
126123
const {
127124
ProtocolGlobalParams: { UsdAddress },
128125
LendingRateOracleRatesCommon,
@@ -134,3 +131,15 @@ export const getLendingRateOracles = (poolConfig: ICommonConfiguration) => {
134131
Object.keys(ReserveAssets[network]).includes(key)
135132
);
136133
};
134+
135+
export const getQuoteCurrency = async (config: IBaseConfiguration) => {
136+
switch (config.OracleQuoteCurrency) {
137+
case 'ETH':
138+
case 'WETH':
139+
return getWethAddress(config);
140+
case 'USD':
141+
return config.ProtocolGlobalParams.UsdAddress;
142+
default:
143+
throw `Quote ${config.OracleQuoteCurrency} currency not set. Add a new case to getQuoteCurrency switch`;
144+
}
145+
};

helpers/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import BigNumber from 'bignumber.js';
2+
import { eEthereumNetwork } from './types';
23

34
// ----------------
45
// MATH
@@ -12,6 +13,7 @@ export const RAY = new BigNumber(10).exponentiatedBy(27).toFixed();
1213
export const HALF_RAY = new BigNumber(RAY).multipliedBy(0.5).toFixed();
1314
export const WAD_RAY_RATIO = Math.pow(10, 9).toString();
1415
export const oneEther = new BigNumber(Math.pow(10, 18));
16+
export const oneUsd = new BigNumber(Math.pow(10, 8));
1517
export const oneRay = new BigNumber(Math.pow(10, 27));
1618
export const MAX_UINT_AMOUNT =
1719
'115792089237316195423570985008687907853269984665640564039457584007913129639935';

0 commit comments

Comments
 (0)