Skip to content

Commit 95e9c35

Browse files
kartojalmiguelmtzinf
authored andcommitted
feat: support custom quote currencies. Added base scripts for custom atokens and rates.
1 parent 14fe2ba commit 95e9c35

25 files changed

+593
-308
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
@@ -102,7 +102,7 @@ const buidlerConfig: HardhatUserConfig = {
102102
kovan: getCommonNetworkConfig(eEthereumNetwork.kovan, 42),
103103
ropsten: getCommonNetworkConfig(eEthereumNetwork.ropsten, 3),
104104
main: getCommonNetworkConfig(eEthereumNetwork.main, 1),
105-
tenderlyMain: getCommonNetworkConfig(eEthereumNetwork.tenderlyMain, 3030),
105+
tenderly: getCommonNetworkConfig(eEthereumNetwork.tenderly, 3030),
106106
matic: getCommonNetworkConfig(ePolygonNetwork.matic, 137),
107107
mumbai: getCommonNetworkConfig(ePolygonNetwork.mumbai, 80001),
108108
xdai: getCommonNetworkConfig(eXDaiNetwork.xdai, 100),

helper-hardhat-config.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const NETWORKS_RPC_URL: iParamsPerNetwork<string> = {
4646
[eEthereumNetwork.coverage]: 'http://localhost:8555',
4747
[eEthereumNetwork.hardhat]: 'http://localhost:8545',
4848
[eEthereumNetwork.buidlerevm]: 'http://localhost:8545',
49-
[eEthereumNetwork.tenderlyMain]: `https://rpc.tenderly.co/fork/${TENDERLY_FORK_ID}`,
49+
[eEthereumNetwork.tenderly]: `https://rpc.tenderly.co/fork/`,
5050
[ePolygonNetwork.mumbai]: 'https://rpc-mumbai.maticvigil.com',
5151
[ePolygonNetwork.matic]: 'https://rpc-mainnet.matic.network',
5252
[eXDaiNetwork.xdai]: 'https://rpc.xdaichain.com/',
@@ -61,7 +61,7 @@ export const NETWORKS_DEFAULT_GAS: iParamsPerNetwork<number> = {
6161
[eEthereumNetwork.coverage]: 65 * GWEI,
6262
[eEthereumNetwork.hardhat]: 65 * GWEI,
6363
[eEthereumNetwork.buidlerevm]: 65 * GWEI,
64-
[eEthereumNetwork.tenderlyMain]: 0.01 * GWEI,
64+
[eEthereumNetwork.tenderly]: 1 * GWEI,
6565
[ePolygonNetwork.mumbai]: 1 * GWEI,
6666
[ePolygonNetwork.matic]: 1 * GWEI,
6767
[eXDaiNetwork.xdai]: 1 * GWEI,
@@ -76,7 +76,7 @@ export const BLOCK_TO_FORK: iParamsPerNetwork<number | undefined> = {
7676
[eEthereumNetwork.coverage]: undefined,
7777
[eEthereumNetwork.hardhat]: undefined,
7878
[eEthereumNetwork.buidlerevm]: undefined,
79-
[eEthereumNetwork.tenderlyMain]: 12406069,
79+
[eEthereumNetwork.tenderly]: undefined,
8080
[ePolygonNetwork.mumbai]: undefined,
8181
[ePolygonNetwork.matic]: undefined,
8282
[eXDaiNetwork.xdai]: undefined,

helpers/configuration.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ 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 AvalancheConfig from '../markets/avalanche';
1313
import AmmConfig from '../markets/amm';
14+
1415
import { CommonsConfig } from '../markets/aave/commons';
1516
import { DRE, filterMapBy } from './misc-utils';
1617
import { tEthereumAddress } from './types';
@@ -66,7 +67,7 @@ export const getReservesConfigByPool = (pool: AavePools): iMultiPoolsAssets<IRes
6667
);
6768

6869
export const getGenesisPoolAdmin = async (
69-
config: ICommonConfiguration
70+
config: IBaseConfiguration
7071
): Promise<tEthereumAddress> => {
7172
const currentNetwork = process.env.FORK ? process.env.FORK : DRE.network.name;
7273
const targetAddress = getParamPerNetwork(config.PoolAdmin, <eNetwork>currentNetwork);
@@ -78,9 +79,7 @@ export const getGenesisPoolAdmin = async (
7879
return addressList[addressIndex];
7980
};
8081

81-
export const getEmergencyAdmin = async (
82-
config: ICommonConfiguration
83-
): Promise<tEthereumAddress> => {
82+
export const getEmergencyAdmin = async (config: IBaseConfiguration): Promise<tEthereumAddress> => {
8483
const currentNetwork = process.env.FORK ? process.env.FORK : DRE.network.name;
8584
const targetAddress = getParamPerNetwork(config.EmergencyAdmin, <eNetwork>currentNetwork);
8685
if (targetAddress) {
@@ -91,19 +90,17 @@ export const getEmergencyAdmin = async (
9190
return addressList[addressIndex];
9291
};
9392

94-
export const getTreasuryAddress = async (
95-
config: ICommonConfiguration
96-
): Promise<tEthereumAddress> => {
93+
export const getTreasuryAddress = async (config: IBaseConfiguration): Promise<tEthereumAddress> => {
9794
const currentNetwork = process.env.FORK ? process.env.FORK : DRE.network.name;
9895
return getParamPerNetwork(config.ReserveFactorTreasuryAddress, <eNetwork>currentNetwork);
9996
};
10097

10198
export const getATokenDomainSeparatorPerNetwork = (
10299
network: eNetwork,
103-
config: ICommonConfiguration
100+
config: IBaseConfiguration
104101
): tEthereumAddress => getParamPerNetwork<tEthereumAddress>(config.ATokenDomainSeparator, network);
105102

106-
export const getWethAddress = async (config: ICommonConfiguration) => {
103+
export const getWethAddress = async (config: IBaseConfiguration) => {
107104
const currentNetwork = process.env.FORK ? process.env.FORK : DRE.network.name;
108105
const wethAddress = getParamPerNetwork(config.WETH, <eNetwork>currentNetwork);
109106
if (wethAddress) {
@@ -116,7 +113,7 @@ export const getWethAddress = async (config: ICommonConfiguration) => {
116113
return weth.address;
117114
};
118115

119-
export const getWrappedNativeTokenAddress = async (config: ICommonConfiguration) => {
116+
export const getWrappedNativeTokenAddress = async (config: IBaseConfiguration) => {
120117
const currentNetwork = process.env.MAINNET_FORK === 'true' ? 'main' : DRE.network.name;
121118
const wethAddress = getParamPerNetwork(config.WrappedNativeToken, <eNetwork>currentNetwork);
122119
if (wethAddress) {
@@ -129,7 +126,7 @@ export const getWrappedNativeTokenAddress = async (config: ICommonConfiguration)
129126
return weth.address;
130127
};
131128

132-
export const getLendingRateOracles = (poolConfig: ICommonConfiguration) => {
129+
export const getLendingRateOracles = (poolConfig: IBaseConfiguration) => {
133130
const {
134131
ProtocolGlobalParams: { UsdAddress },
135132
LendingRateOracleRatesCommon,
@@ -141,3 +138,15 @@ export const getLendingRateOracles = (poolConfig: ICommonConfiguration) => {
141138
Object.keys(ReserveAssets[network]).includes(key)
142139
);
143140
};
141+
142+
export const getQuoteCurrency = async (config: IBaseConfiguration) => {
143+
switch (config.OracleQuoteCurrency) {
144+
case 'ETH':
145+
case 'WETH':
146+
return getWethAddress(config);
147+
case 'USD':
148+
return config.ProtocolGlobalParams.UsdAddress;
149+
default:
150+
throw `Quote ${config.OracleQuoteCurrency} currency not set. Add a new case to getQuoteCurrency switch`;
151+
}
152+
};

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)