HyperEVM Development · L1 Precompiles & Interop · 4/8
Reading L1 State from HyperEVM
Read Precompiles
The most powerful feature of HyperEVM is the ability to read HyperCore state from Solidity contracts. This is done through read precompiles — special addresses starting at 0x0000000000000000000000000000000000000800 that execute native code instead of EVM bytecode. The official interface is the L1Read.sol file attached to Interacting with HyperCore.
Available Precompiles
A selection of the read precompiles (as of October 2026):
| Address | Reads | Input |
|---|---|---|
0x…0800 |
Perp position (size, entry notional, leverage) | (address user, uint16 perp) |
0x…0801 |
Spot balance | (address user, uint64 token) |
0x…0802 |
User's equity in a vault | (address user, address vault) |
0x…0806 |
Mark price | uint32 perpIndex |
0x…0807 |
Oracle price | uint32 perpIndex |
0x…0808 |
Spot price | uint32 spotIndex |
0x…080e |
Best bid/offer | uint32 asset |
0x…080F |
Account margin summary (account value, margin used) | (uint32 perpDexIndex, address user) |
Assets are identified by index, not by name — look the index up in the meta / spotMeta info endpoints (on mainnet, BTC is perp 0). There is no funding-rate or full order-book-depth precompile; only best bid/offer is exposed.
Reading Mark and Oracle Prices
Precompiles are called with staticcall and ABI-encoded inputs:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract PriceReader {
address constant MARK_PX = 0x0000000000000000000000000000000000000806;
address constant ORACLE_PX = 0x0000000000000000000000000000000000000807;
function markPx(uint32 perpIndex) public view returns (uint64) {
(bool ok, bytes memory res) = MARK_PX.staticcall(abi.encode(perpIndex));
require(ok, "markPx failed");
return abi.decode(res, (uint64));
}
function oraclePx(uint32 perpIndex) public view returns (uint64) {
(bool ok, bytes memory res) = ORACLE_PX.staticcall(abi.encode(perpIndex));
require(ok, "oraclePx failed");
return abi.decode(res, (uint64));
}
function priceDivergence(uint32 perpIndex) external view returns (int256) {
return int256(uint256(markPx(perpIndex))) - int256(uint256(oraclePx(perpIndex)));
}
}
Prices are integers: divide a perp price by 10^(6 - szDecimals) to get a human-readable number (spot: 10^(8 - base szDecimals)).
Reading Account State
contract MarginReader {
address constant ACCOUNT_MARGIN_SUMMARY = 0x000000000000000000000000000000000000080F;
struct AccountMarginSummary {
int64 accountValue;
uint64 marginUsed;
uint64 ntlPos;
int64 rawUsd;
}
function summary(address user) public view returns (AccountMarginSummary memory) {
// perpDexIndex 0 = the default (validator-operated) perp dex
(bool ok, bytes memory res) =
ACCOUNT_MARGIN_SUMMARY.staticcall(abi.encode(uint32(0), user));
require(ok, "margin summary failed");
return abi.decode(res, (AccountMarginSummary));
}
function isLeveraged(address user, uint256 maxRatioPct) external view returns (bool) {
AccountMarginSummary memory s = summary(user);
if (s.accountValue <= 0) return false;
return (uint256(s.marginUsed) * 100) / uint256(uint64(s.accountValue)) > maxRatioPct;
}
}
Use Cases for HyperCore Reads
1. Collateral Valuation
A lending protocol can value collateral with HyperCore oracle or spot prices.
2. Automated Vault Strategies
Smart contracts can read prices and their own HyperCore positions, then act through the CoreWriter system contract (covered later).
3. Risk Monitoring
DeFi protocols can check users' leverage on HyperCore to adjust parameters.
4. Conditional Execution
Execute HyperEVM logic only when HyperCore conditions are met (e.g., mark price diverges from oracle price).
Important Caveats
- Values match the latest HyperCore state at the time the EVM block is built — not live within the block
- Calls with invalid inputs (e.g., a bad asset index or vault address) revert and consume all gas passed to the call
- Not all HyperCore state is exposed — only the precompiles listed in
L1Read.sol - The precompile set changes over time — always check the latest docs
Gas Considerations
Per the docs, a precompile call costs:
2000 + 65 * (input_len + output_len) // lengths in bytes
For comparison, a cold SLOAD costs 2,100 gas and a warm one 100 gas.