This repository has been archived by the owner on Mar 3, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
BalancerLPComposableStableEthOracle.sol
74 lines (59 loc) · 3.21 KB
/
BalancerLPComposableStableEthOracle.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2023 Tokemak Foundation. All rights reserved.
pragma solidity 0.8.17;
import { Errors } from "src/utils/Errors.sol";
import { BalancerUtilities } from "src/libs/BalancerUtilities.sol";
import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol";
import { IPriceOracle } from "src/interfaces/oracles/IPriceOracle.sol";
import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import { IVault as IBalancerVault } from "src/interfaces/external/balancer/IVault.sol";
import { IBalancerComposableStablePool } from "src/interfaces/external/balancer/IBalancerComposableStablePool.sol";
import { SystemComponent } from "src/SystemComponent.sol";
/// @title Price oracle for Balancer Composable Stable pools
/// @dev getPriceEth is not a view fn to support reentrancy checks. Dont actually change state.
contract BalancerLPComposableStableEthOracle is SystemComponent, IPriceOracle {
/// @notice The Balancer Vault that all tokens we're resolving here should reference
/// @dev BPTs themselves are configured with an immutable vault reference
IBalancerVault public immutable balancerVault;
error InvalidPrice(address token, uint256 price);
constructor(ISystemRegistry _systemRegistry, IBalancerVault _balancerVault) SystemComponent(_systemRegistry) {
// System registry must be properly initialized first
Errors.verifyNotZero(address(_systemRegistry.rootPriceOracle()), "rootPriceOracle");
Errors.verifyNotZero(address(_balancerVault), "_balancerVault");
balancerVault = _balancerVault;
}
/// @inheritdoc IPriceOracle
function getPriceInEth(address token) external returns (uint256 price) {
Errors.verifyNotZero(token, "token");
BalancerUtilities.checkReentrancy(address(balancerVault));
IBalancerComposableStablePool pool = IBalancerComposableStablePool(token);
bytes32 poolId = pool.getPoolId();
// Will revert with BAL#500 on invalid pool id
// Partial return values are intentionally ignored. This call provides the most efficient way to get the data.
// slither-disable-next-line unused-return
(IERC20[] memory tokens,,) = balancerVault.getPoolTokens(poolId);
uint256 bptIndex = pool.getBptIndex();
uint256 minPrice = type(uint256).max;
uint256 nTokens = tokens.length;
for (uint256 i = 0; i < nTokens;) {
if (i != bptIndex) {
// Our prices are always in 1e18
uint256 tokenPrice = systemRegistry.rootPriceOracle().getPriceInEth(address(tokens[i]));
tokenPrice = tokenPrice * 1e18 / pool.getTokenRate(tokens[i]);
if (tokenPrice < minPrice) {
minPrice = tokenPrice;
}
}
unchecked {
++i;
}
}
// If it's still the default vault we set, something went wrong
if (minPrice == type(uint256).max) {
revert InvalidPrice(token, type(uint256).max);
}
// Intentional precision loss, prices should always be in e18
// slither-disable-next-line divide-before-multiply
price = (minPrice * pool.getRate()) / 1e18;
}
}