SharePriceOracle
Cross-Chain ERC4626 Vault Share Price Oracle
Overview
SharePriceOracle is a multi-adapter oracle system designed for cross-chain ERC4626 vault share price propagation. The system enables protocols to access and share vault share prices across different blockchains using LayerZero as a messaging layer. It provides a unified interface for fetching accurate price data from various sources and serves as a critical infrastructure for cross-chain vault strategies, yield aggregators, and DeFi protocols.
Key Features
Multi-adapter architecture: Supports multiple price feed providers (Chainlink, Pyth, API3, Redstone, AMMs)
Cross-chain compatibility: Propagates vault share prices across different blockchains via LayerZero
Price normalization: Converts prices to standardized 18-decimal precision (WAD)
Fallback mechanisms: Implements priority-based fallback for price sources
Price validation: Enforces bounds checking and staleness prevention
L2 sequencer awareness: Handles L2 sequencer downtime gracefully
System Architecture
The SharePriceOracle system consists of three main components:
SharePriceOracle: The core contract that serves as the router and price aggregator
Oracle Adapters: Specialized contracts for interfacing with different price feed providers
MaxLzEndpoint: LayerZero endpoint for cross-chain communication
Data Flow
┌───────────────────┐ ┌───────────────────┐
│ SharePriceOracle │ │ SharePriceOracle │
│ (Chain A) │◄─────────┤ (Chain B) │
└─────────┬─────────┘ └─────────┬─────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ Oracle Adapters │ │ Oracle Adapters │
│ (Chainlink, Pyth, │ │ (Chainlink, Pyth, │
│ UniswapV3, etc.) │ │ UniswapV3, etc.) │
└─────────────────────┘ └─────────────────────┘
Core Contracts
SharePriceOracle
The SharePriceOracle is the central contract that:
Manages a registry of price adapters
Handles cross-chain asset mappings
Stores and updates share prices
Provides normalized price conversion
Enforces access control for different operations
contract SharePriceOracle is ISharePriceOracle, OwnableRoles {
// Roles
uint256 public constant ADMIN_ROLE = _ROLE_0;
uint256 public constant ENDPOINT_ROLE = _ROLE_1;
uint256 public constant ADAPTER_ROLE = _ROLE_2;
// Mappings
mapping(address => mapping(uint8 => LocalAssetConfig)) public localAssetConfigs;
mapping(address => StoredSharePrice) public storedSharePrices;
mapping(bytes32 => address) public crossChainAssetMap;
mapping(bytes32 => VaultReport) public sharePrices;
}
Key functions:
getSharePrices
: Fetches current share prices for multiple vaultsupdateSharePrices
: Updates share prices received from other chainsgetLatestSharePrice
: Gets the latest share price for a specific vaultgetPrice
: Gets the price of an asset from available adapterssetLocalAssetConfig
: Configures a price feed for a local assetsetCrossChainAssetMapping
: Maps cross-chain assets to local equivalents
MaxLzEndpoint
The MaxLzEndpoint contract handles cross-chain messaging via LayerZero:
Sends vault share prices to other chains
Receives and processes share price updates
Manages messaging options and peers
Implements request-response patterns for price updates
contract MaxLzEndpoint is ILayerZeroReceiver, Ownable {
// Message pattern identifiers
uint8 private constant AB_TYPE = 1; // Simple send-receive
uint8 private constant ABA_TYPE = 2; // Request-response pattern
// Mappings
mapping(uint32 eid => bytes32 peer) public peers;
}
Key functions:
sendSharePrices
: Sends share prices to a destination chainrequestSharePrices
: Requests share prices from a destination chainlzReceive
: Processes incoming messages from LayerZero
Oracle Adapters
The system supports multiple oracle adapters, each specialized for a different price feed provider:
1. Chainlink Adapter
Fetches price data from Chainlink price feeds with heartbeat validation and price bounds checking.
contract ChainlinkAdapter is BaseOracleAdapter {
function getPrice(address asset, bool inUSD)
external view override returns (ISharePriceOracle.PriceReturnData memory);
}
2. Pyth Adapter
Integrates with Pyth Network for high-frequency, decentralized price updates.
contract PythAdapter is BaseOracleAdapter {
function getPrice(address asset, bool inUSD)
external view override returns (ISharePriceOracle.PriceReturnData memory);
function updateAndGetPrice(address asset, bool inUSD, bytes[] calldata updateData)
external payable returns (ISharePriceOracle.PriceReturnData memory);
}
3. API3 Adapter
Uses API3's dAPI (data feed) system for price data.
contract Api3Adapter is BaseOracleAdapter {
function getPrice(address asset, bool inUSD)
external view override returns (ISharePriceOracle.PriceReturnData memory);
}
4. Redstone Adapter
Integrates with Redstone's oracle network for price data.
contract RedStoneAdapter is BaseOracleAdapter {
function getPrice(address asset, bool inUSD)
external view override returns (ISharePriceOracle.PriceReturnData memory);
}
5. AMM-based Adapters
Several adapters for extracting prices from AMM pools:
UniswapV3Adapter: Uses TWAP (Time-Weighted Average Price) from Uniswap V3 pools
BalancerAdapter: Extracts prices from Balancer weighted pools
AerodromeV1Adapter/AerodromeV2Adapter: Works with Aerodrome pools (Base-specific)
Curve2PoolAssetAdapter: Extracts prices from Curve pools with reentrancy protection
Key Data Structures
VaultReport
Contains vault share price information and metadata for cross-chain communication:
struct VaultReport {
uint256 sharePrice;
uint64 lastUpdate;
uint32 chainId;
address rewardsDelegate;
address vaultAddress;
address asset;
uint256 assetDecimals;
}
PriceReturnData
Standardized structure for price data returned by adapters:
struct PriceReturnData {
uint240 price; // Price normalized to WAD (1e18)
bool hadError; // Error flag
bool inUSD; // Price denomination
}
LocalAssetConfig
Configuration for local assets (e.g., USDC, WETH):
struct LocalAssetConfig {
address priceFeed; // Priority-ordered price feeds
bool inUSD; // Whether price should be in USD
address adaptor; // Adapter address
}
Security Considerations
The SharePriceOracle implements several security mechanisms:
Access control: Role-based permissions for administrative and update operations
Price validation: Minimum and maximum bounds for prices to prevent manipulation
Staleness prevention: Timestamp-based checks to ensure price freshness
Sequencer awareness: Checks for L2 sequencer status before using prices
Multiple price sources: Fallback mechanism to increase resilience
Common Workflows
Adding a New Vault to Monitor
To start monitoring a new vault's share price:
Ensure the vault's underlying asset has a price feed configured
Call
getSharePrices()
with the vault address to get its current share priceUse
sendSharePrices()
on the MaxLzEndpoint to propagate the price to other chains
Configuring a New Price Feed
To add a new price feed for an asset:
Deploy and configure the appropriate adapter contract
Grant the adapter the ADAPTER_ROLE
Call
setLocalAssetConfig()
to register the asset with the adapter
Cross-Chain Asset Mapping
To enable price conversion for cross-chain assets:
Identify the local equivalent of the cross-chain asset
Call
setCrossChainAssetMapping()
with the source chain ID, source asset, and local asset
Error Handling
The system uses custom errors to provide clear failure reasons:
InvalidAsset
: Asset not supported or not properly configuredNoValidPrice
: Unable to get a valid priceStalePrice
: Price is too old to be considered validSequencerUnavailable
: L2 sequencer is down or in grace periodMessageAlreadyProcessed
: Prevents duplicate processing of cross-chain messages
Glossary
WAD: Standard unit representing 1 with 18 decimal places (10^18)
EID: Endpoint ID in LayerZero, representing a specific blockchain
TWAP: Time-Weighted Average Price, used by DEX-based oracles
ERC4626: Token standard for tokenized vaults
Share Price: The price of one vault share expressed in terms of the underlying asset
Last updated