@ubk-labs/ubk-oracle 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7,6 +7,7 @@ import "@openzeppelin/contracts/interfaces/IERC4626.sol";
|
|
|
7
7
|
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
8
8
|
|
|
9
9
|
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
|
|
10
|
+
import "@ubk-labs/ubk-commons/contracts/abstract/UBKDecimalsBounded.sol";
|
|
10
11
|
|
|
11
12
|
import "../../interfaces/IUBKOracle.sol";
|
|
12
13
|
import "../errors/UBKOracleErrors.sol";
|
|
@@ -39,7 +40,7 @@ import "../constants/UBKOracleConstants.sol";
|
|
|
39
40
|
* - UI / Subgraphs can use `isPriceFresh()` and `getPriceAge()` for safety checks.
|
|
40
41
|
*
|
|
41
42
|
*/
|
|
42
|
-
contract UBKOracle is IUBKOracle, Ownable {
|
|
43
|
+
contract UBKOracle is IUBKOracle, UBKDecimalsBounded, Ownable {
|
|
43
44
|
// -----------------------------------------------------------------------
|
|
44
45
|
// Storage
|
|
45
46
|
// -----------------------------------------------------------------------
|
|
@@ -87,10 +88,7 @@ contract UBKOracle is IUBKOracle, Ownable {
|
|
|
87
88
|
* @notice Deploys the Oracle contract.
|
|
88
89
|
* @param _owner The address to assign as the owner (governance or deployer).
|
|
89
90
|
*/
|
|
90
|
-
constructor(address _owner) Ownable(_owner) {
|
|
91
|
-
if (_owner == address(0))
|
|
92
|
-
revert ZeroAddress("UBKOracle::constructor", "owner");
|
|
93
|
-
}
|
|
91
|
+
constructor(address _owner) Ownable(_owner) {}
|
|
94
92
|
|
|
95
93
|
/// @notice Ensures oracle is not paused.
|
|
96
94
|
modifier whenNotPaused() {
|
|
@@ -240,9 +238,7 @@ contract UBKOracle is IUBKOracle, Ownable {
|
|
|
240
238
|
* @dev Ensures decimals ≤ 18 and feed returns a nonzero updatedAt value.
|
|
241
239
|
*/
|
|
242
240
|
function setChainlinkFeed(address token, address feed) external onlyOwner {
|
|
243
|
-
|
|
244
|
-
revert ZeroAddress("UBKOracle::setChainlinkFeed", "input");
|
|
245
|
-
if (feed.code.length == 0) revert InvalidFeedContract(feed);
|
|
241
|
+
_validateChainlinkFeed(token, feed);
|
|
246
242
|
|
|
247
243
|
AggregatorV3Interface agg = AggregatorV3Interface(feed);
|
|
248
244
|
uint8 decimals = agg.decimals();
|
|
@@ -276,11 +272,7 @@ contract UBKOracle is IUBKOracle, Ownable {
|
|
|
276
272
|
address vault,
|
|
277
273
|
address underlying
|
|
278
274
|
) external onlyOwner {
|
|
279
|
-
|
|
280
|
-
revert ZeroAddress("UBKOracle::setERC4626Vault", "input");
|
|
281
|
-
try IERC4626(vault).asset() returns (address) {} catch {
|
|
282
|
-
revert InvalidERC4626Vault(vault);
|
|
283
|
-
}
|
|
275
|
+
_validateERC4626Vault(vault, underlying);
|
|
284
276
|
erc4626Underlying[vault] = underlying;
|
|
285
277
|
_addSupportedToken(vault);
|
|
286
278
|
emit ERC4626Registered(vault, underlying);
|
|
@@ -312,6 +304,36 @@ contract UBKOracle is IUBKOracle, Ownable {
|
|
|
312
304
|
return _fetchAndUpdatePrice(token);
|
|
313
305
|
}
|
|
314
306
|
|
|
307
|
+
/**
|
|
308
|
+
* @notice Batch price fetch & update for multiple tokens.
|
|
309
|
+
* @param tokens Array of asset token addresses.
|
|
310
|
+
* @return prices Array of fresh prices in 1e18 precision.
|
|
311
|
+
*
|
|
312
|
+
* @dev
|
|
313
|
+
* - Runs whenNotPaused modifier.
|
|
314
|
+
* - Reverts if any token is zero address.
|
|
315
|
+
* - Each iteration calls the same internal `_fetchAndUpdatePrice`.
|
|
316
|
+
* - Gas-efficient: msg.sender checked once, calldata parsed once.
|
|
317
|
+
*/
|
|
318
|
+
function fetchAndUpdatePrice(
|
|
319
|
+
address[] calldata tokens
|
|
320
|
+
) external whenNotPaused returns (uint256[] memory prices) {
|
|
321
|
+
uint256 len = tokens.length;
|
|
322
|
+
prices = new uint256[](len);
|
|
323
|
+
|
|
324
|
+
for (uint256 i = 0; i < len; ++i) {
|
|
325
|
+
address token = tokens[i];
|
|
326
|
+
if (token == address(0)) {
|
|
327
|
+
revert ZeroAddress(
|
|
328
|
+
"UBKOracle::fetchAndUpdatePriceBatch",
|
|
329
|
+
"token"
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
prices[i] = _fetchAndUpdatePrice(token);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
315
337
|
/**
|
|
316
338
|
* @notice Returns age of last cached price in seconds.
|
|
317
339
|
* @param token Token address.
|
|
@@ -415,7 +437,7 @@ contract UBKOracle is IUBKOracle, Ownable {
|
|
|
415
437
|
return (lv.timestamp != 0 &&
|
|
416
438
|
block.timestamp - lv.timestamp <= stalePeriod[token]);
|
|
417
439
|
}
|
|
418
|
-
|
|
440
|
+
|
|
419
441
|
/**
|
|
420
442
|
* @notice Resolves the fair price of an ERC4626 vault share.
|
|
421
443
|
* @param vault ERC4626 vault token address.
|
|
@@ -582,4 +604,52 @@ contract UBKOracle is IUBKOracle, Ownable {
|
|
|
582
604
|
emit TokenSupportAdded(token);
|
|
583
605
|
}
|
|
584
606
|
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* @notice Validates assets and their associated Chainlink feeds before mutating system state.
|
|
610
|
+
* @param token Asset token address.
|
|
611
|
+
* @param feed Chainlink AggregatorV3 feed address.
|
|
612
|
+
* @dev Ensures decimals ≤ 18 and feed returns a nonzero updatedAt value.
|
|
613
|
+
*/
|
|
614
|
+
function _validateChainlinkFeed(address token, address feed) internal view {
|
|
615
|
+
if (token == address(0))
|
|
616
|
+
revert ZeroAddress("UBKOracle::setChainlinkFeed", "token");
|
|
617
|
+
if (feed == address(0))
|
|
618
|
+
revert ZeroAddress("UBKOracle::setChainlinkFeed", "feed");
|
|
619
|
+
if (feed.code.length == 0) revert InvalidFeedContract(feed);
|
|
620
|
+
_validateTokenDecimals(token);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* @notice Validates input parameters before setting an ERc 4626 vault.
|
|
625
|
+
* @dev Ensures decimals for vault and underlying assets are equal, and bounded by global invariants.
|
|
626
|
+
* @param vault ERC-4626 asset.
|
|
627
|
+
* @param underlying ERC-20 asset.
|
|
628
|
+
*/
|
|
629
|
+
function _validateERC4626Vault(
|
|
630
|
+
address vault,
|
|
631
|
+
address underlying
|
|
632
|
+
) internal view {
|
|
633
|
+
if (vault == address(0))
|
|
634
|
+
revert ZeroAddress("UBKOracle::_validateERC4626Vault", "vault");
|
|
635
|
+
if (underlying == address(0)) {
|
|
636
|
+
revert ZeroAddress(
|
|
637
|
+
"UBKOracle::_validateERC4626Vault",
|
|
638
|
+
"underlying"
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
try IERC4626(vault).asset() returns (address) {} catch {
|
|
642
|
+
revert InvalidERC4626Vault(vault);
|
|
643
|
+
}
|
|
644
|
+
uint8 vaultDecimals = _validateTokenDecimals(vault); // Must be between [6,18]
|
|
645
|
+
uint8 underlyingDecimals = _validateTokenDecimals(underlying); // Must be between [6,18]
|
|
646
|
+
|
|
647
|
+
if (vaultDecimals != underlyingDecimals) {
|
|
648
|
+
revert ERC4626DecimalsMismatch(
|
|
649
|
+
"UBKOracle::_validateERC4626Vault",
|
|
650
|
+
vault,
|
|
651
|
+
underlying
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
585
655
|
}
|
|
@@ -19,3 +19,4 @@ error NoFallbackPrice(address token);
|
|
|
19
19
|
error SuspiciousVaultRate(address vault, uint256 rate);
|
|
20
20
|
error RecursiveResolution(address token);
|
|
21
21
|
error OraclePaused(address oracle, uint256 timestamp);
|
|
22
|
+
error ERC4626DecimalsMismatch(string functionName, address vault, address underlying);
|
|
@@ -94,7 +94,9 @@ interface IUBKOracle {
|
|
|
94
94
|
* @dev Keeper entrypoint. May revert if price resolution fails.
|
|
95
95
|
*/
|
|
96
96
|
function fetchAndUpdatePrice(address token) external returns (uint256);
|
|
97
|
-
|
|
97
|
+
function fetchAndUpdatePrice(
|
|
98
|
+
address[] calldata tokens
|
|
99
|
+
) external returns (uint256[] memory);
|
|
98
100
|
// -----------------------------------------------------------------------
|
|
99
101
|
// ADMIN / GOVERNANCE CONFIGURATION
|
|
100
102
|
// -----------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ubk-labs/ubk-oracle",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Oracle supporting ERC-20 and ERC-4626 assets for use in decentralized financial applications.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npx hardhat compile",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"contracts",
|
|
22
22
|
"interfaces",
|
|
23
|
+
"deployments",
|
|
23
24
|
"README.md",
|
|
24
25
|
"LICENSE",
|
|
25
26
|
"package.json"
|
|
@@ -37,6 +38,6 @@
|
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
40
|
"@chainlink/contracts": "^0.6.1",
|
|
40
|
-
"@ubk-labs/ubk-commons": "^0.1.
|
|
41
|
+
"@ubk-labs/ubk-commons": "^0.1.7"
|
|
41
42
|
}
|
|
42
43
|
}
|