Description:
Decentralized Finance (DeFi) protocol contract providing Factory, Oracle functionality.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/oracle/ChainlinkOracleFeed.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import {OracleFeed} from "./OracleFeed.sol";
import {Errors} from "../libraries/Errors.sol";
/// @title ChainlinkOracleFeed
/// @author luoyhang003
/// @notice Oracle adapter that integrates with Chainlink price feeds.
/// @dev Inherits from the abstract OracleFeed contract and implements the
/// peek function by querying a Chainlink AggregatorV3Interface.
contract ChainlinkOracleFeed is OracleFeed {
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////////////////*/
/// @notice Chainlink price feed contract (AggregatorV3Interface).
/// @dev Immutable after deployment to ensure oracle integrity.
AggregatorV3Interface public immutable priceFeed;
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
/// @notice Deploys the Chainlink oracle feed wrapper.
/// @param _token The address of the token this oracle is associated with.
/// @param _name A human-readable name for this oracle feed.
/// @param _priceFeed The address of the Chainlink AggregatorV3 price feed.
constructor(
address _token,
string memory _name,
address _priceFeed
) OracleFeed(_token, _name) {
if (_priceFeed == address(0)) revert Errors.ZeroAddress();
priceFeed = AggregatorV3Interface(_priceFeed);
}
/*//////////////////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Returns the latest price from the Chainlink feed, normalized to 18 decimals.
/// @dev Performs several safety checks:
/// - Ensures the returned price is positive.
/// - Ensures the round is not stale.
/// - Ensures the round data is complete and not from the future.
/// - Adjusts decimals from the Chainlink feed to a standard 18-decimal format.
/// @return price_ The latest valid price, scaled to 18 decimals.
function peek() external view override returns (uint256 price_) {
(
uint80 roundId,
int256 answer,
,
uint256 updatedAt,
uint80 answeredInRound
) = priceFeed.latestRoundData();
if (answer <= 0) revert Errors.InvalidPrice();
if (answeredInRound < roundId) revert Errors.StalePrice();
if (updatedAt == 0 || updatedAt > block.timestamp)
revert Errors.IncompleteRound();
uint8 decimals = priceFeed.decimals();
// Convert int256 to uint256 after validation, and normalize to 18 decimals
if (decimals > 18) {
// scale down
return uint256(answer) / (10 ** (decimals - 18));
} else {
// scale up
return uint256(answer) * (10 ** (18 - decimals));
}
}
}
"
},
"lib/chainlink-evm/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
"
},
"src/oracle/OracleFeed.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;
/// @title OracleFeed
/// @author luoyhang003
/// @notice Abstract contract for oracle price feeds. Provides a standard interface
/// for retrieving asset prices.
/// @dev This contract is meant to be inherited by specific oracle implementations
/// that define the logic for fetching asset prices.
abstract contract OracleFeed {
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////////////////*/
/// @notice The token address associated with this oracle feed.
/// @dev Immutable after deployment to ensure the oracle is tied to a specific token.
address public immutable token;
/// @notice The human-readable name of the oracle feed.
/// @dev Can be used for UI or off-chain identification purposes.
string public name;
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
/// @notice Sets the token address and feed name during deployment.
/// @param _token The address of the token this oracle is associated with.
/// @param _name The descriptive name for this oracle feed.
constructor(address _token, string memory _name) {
token = _token;
name = _name;
}
/*//////////////////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Returns the current price of the specified asset.
/// @dev Implementations must override this function to provide actual price logic.
/// @return price_ The price of the asset with 18 decimals of precision.
function peek() external view virtual returns (uint256 price_);
}
"
},
"src/libraries/Errors.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;
/// @title Errors Library
/// @author luoyhang003
/// @notice Centralized custom errors for all contracts in the protocol.
/// @dev Each error saves gas compared to revert strings. The @dev comment
/// also includes the corresponding 4-byte selector for debugging
/// and off-chain tooling.
library Errors {
/*//////////////////////////////////////////////////////////////////////////
GENERAL
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when array lengths do not match or are zero.
/// @dev Selector: 0x9d89020a
error InvalidArrayLength();
/// @notice Thrown when a provided address is zero.
/// @dev Selector: 0xd92e233d
error ZeroAddress();
/// @notice Thrown when a provided amount is zero.
/// @dev Selector: 0x1f2a2005
error ZeroAmount();
/// @notice Thrown when an index is out of bounds.
/// @dev Selector: 0x4e23d035
error IndexOutOfBounds();
/// @notice Thrown when a user is not whitelisted but tries to interact.
/// @dev Selector: 0x2ba75b25
error UserNotWhitelisted();
/// @notice Thrown when a token has invalid decimals (e.g., >18).
/// @dev Selector: 0xd25598a0
error InvalidDecimals();
/// @notice Thrown when an asset is not supported.
/// @dev Selector: 0x24a01144
error UnsupportedAsset();
/// @notice Thrown when trying to add an already added asset.
/// @dev Selector: 0x5ed26801
error AssetAlreadyAdded();
/// @notice Thrown when trying to remove an asset that was already removed.
/// @dev Selector: 0x422afd8f
error AssetAlreadyRemoved();
/// @notice Thrown when a common token address conflict with the vault token address.
/// @dev Selector: 0x8a7ea521
error ConflictiveToken();
/// @notice Thrown when an array is reach to the length limit.
/// @dev Selector: 0x951becfa
error ExceedArrayLengthLimit();
/*//////////////////////////////////////////////////////////////////////////
Token.sol
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when transfer is attempted by/for a blacklisted address.
/// @dev Selector: 0x6554e8c5
error TransferBlacklisted();
/*//////////////////////////////////////////////////////////////////////////
DepositVault.sol
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when deposits are paused.
/// @dev Selector: 0x35edea30
error DepositPaused();
/// @notice Thrown when a deposit exceeds per-token deposit cap.
/// @dev Selector: 0xcc60dc5b
error ExceedTokenDepositCap();
/// @notice Thrown when a deposit exceeds global deposit cap.
/// @dev Selector: 0x5054f250
error ExceedTotalDepositCap();
/// @notice Thrown when minting results in zero shares.
/// @dev Selector: 0x1d31001e
error MintZeroShare();
/// @notice Thrown when attempting to deposit zero assets.
/// @dev Selector: 0xd69b89cc
error DepositZeroAsset();
/// @notice Thrown when the oracle for an asset is invalid or missing.
/// @dev Selector: 0x9589a27d
error InvalidOracle();
/*//////////////////////////////////////////////////////////////////////////
WithdrawController.sol
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when withdrawals are paused.
/// @dev Selector: 0xe0a39803
error WithdrawPaused();
/// @notice Thrown when attempting to request withdrawal of zero shares.
/// @dev Selector: 0xef9c351b
error RequestZeroShare();
/// @notice Thrown when a withdrawal receipt has invalid status for the operation.
/// @dev Selector: 0x3bb3ba88
error InvalidReceiptStatus();
/// @notice Thrown when a caller is not the original withdrawal requester.
/// @dev Selector: 0xe39da59e
error NotRequester();
/// @notice Thrown when a caller is blacklisted.
/// @dev Selector: 0x473250af
error UserBlacklisted();
/*//////////////////////////////////////////////////////////////////////////
AssetsRouter.sol
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when a recipient is already added.
/// @dev Selector: 0xce2c4eb3
error RecipientAlreadyAdded();
/// @notice Thrown when a recipient is already removed.
/// @dev Selector: 0x1c7dcc84
error RecipientAlreadyRemoved();
/// @notice Thrown when attempting to set auto-route to its current state.
/// @dev Selector: 0xdf2e473d
error InvalidRouteEnabledStatus();
/// @notice Thrown when a non-registered recipient is used.
/// @dev Selector: 0xf29851db
error NotRouterRecipient();
/// @notice Thrown when attempting to remove the currently active recipient.
/// @dev Selector: 0xa453bd1b
error RemoveActiveRouter();
/// @notice Thrown when attempting to manual route the asstes when auto route is enabled.
/// @dev Selector: 0x92d56bf7
error AutoRouteEnabled();
/// @notice Thrown when setting the same router address.
/// @dev Selector: 0x23b920b6
error SameRouterAddress();
/*//////////////////////////////////////////////////////////////////////////
AccessRegistry.sol
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when trying to blacklist an already blacklisted user.
/// @dev Selector: 0xf53de75f
error AlreadyBlacklisted();
/// @notice Thrown when trying to whitelist an already whitelisted user.
/// @dev Selector: 0xb73e95e1
error AlreadyWhitelisted();
/// @notice Reverts when the requested state matches the current state.
/// @dev Selector: 0x3fbc93f3
error AlreadyInSameState();
/*//////////////////////////////////////////////////////////////////////////
OracleRegister.sol
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when an oracle is already registered for an asset.
/// @dev Selector: 0x652a449e
error OracleAlreadyAdded();
/// @notice Thrown when an oracle does not exist for an asset.
/// @dev Selector: 0x4dca4f7d
error OracleNotExist();
/// @notice Thrown when price deviation exceeds maximum allowed.
/// @dev Selector: 0x8774ad87
error ExceedMaxDeviation();
/// @notice Thrown when price updates are attempted too frequently.
/// @dev Selector: 0x8f46908a
error PriceUpdateTooFrequent();
/// @notice Thrown when a price validity period has expired.
/// @dev Selector: 0x7c9d063a
error PriceValidityExpired();
/// @notice Thrown when a price is zero.
/// @dev Selector: 0x10256287
error InvalidZeroPrice();
/*//////////////////////////////////////////////////////////////////////////
ParamRegister.sol
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when tolerance basis points exceed maximum deviation.
/// @dev Selector: 0xb8d855e2
error ExceedMaxDeviationBps();
/// @notice Thrown when price update interval is invalid.
/// @dev Selector: 0xfff67f52
error InvalidPriceUpdateInterval();
/// @notice Thrown when price validity duration exceeds allowed maximum.
/// @dev Selector: 0x6eca7e24
error PriceMaxValidityExceeded();
/// @notice Thrown when mint fee rate exceeds maximum allowed.
/// @dev Selector: 0x8f59faf8
error ExceedMaxMintFeeRate();
/// @notice Thrown when redeem fee rate exceeds maximum allowed.
/// @dev Selector: 0x86871089
error ExceedMaxRedeemFeeRate();
/*//////////////////////////////////////////////////////////////////////////
ChainlinkOracleFeed.sol
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when a reported price is invalid.
/// @dev Selector: 0x00bfc921
error InvalidPrice();
/// @notice Thrown when a reported price is stale.
/// @dev Selector: 0x19abf40e
error StalePrice();
/// @notice Thrown when a Chainlink round is incomplete.
/// @dev Selector: 0x8ad52bdd
error IncompleteRound();
}
"
}
},
"settings": {
"remappings": [
"@uniswap/v3-periphery/contracts/=lib/v3-periphery/contracts/",
"@chainlink/contracts/src/v0.8/=lib/chainlink-evm/contracts/src/v0.8/shared/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"chainlink-evm/=lib/chainlink-evm/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"v3-periphery/=lib/v3-periphery/contracts/"
],
"optimizer": {
"enabled": false,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false
}
}}
Submitted on: 2025-10-23 16:37:51
Comments
Log in to comment.
No comments yet.