Documentation Index
Fetch the complete documentation index at: https://docs.paxeer.app/llms.txt
Use this file to discover all available pages before exploring further.
Overview
PaxSpot uses four custom EVM precompiles for exchange-critical computation. They run as native HyperPaxeer consensus code and are live on mainnet.
| Address | Name | Purpose |
|---|
0x0000000000000000000000000000000000000901 | OROBResolver | Convert oracle-relative basis-point offsets to absolute prices and back |
0x0000000000000000000000000000000000000902 | BatchClearing | Compute uniform clearing prices for batch auctions |
0x0000000000000000000000000000000000000903 | OracleAggregator / VOM | Read validator oracle prices and submit validator attestations |
0x0000000000000000000000000000000000000904 | PoFQScorer | Score fill quality against oracle prices |
All price values in these examples use 18-decimal fixed point integers.
0x901 OROBResolver
OROB orders store signed basis-point offsets from the oracle price instead of stale absolute prices.
interface IOROBResolver {
function resolveOffset(int256 oraclePrice, int16 offsetBps)
external
view
returns (int256 absolutePrice);
function resolveOffsetBatch(int256 oraclePrice, int16[] calldata offsetsBps)
external
view
returns (int256[] memory absolutePrices);
function toOffset(int256 oraclePrice, int256 absolutePrice)
external
view
returns (int16 offsetBps);
}
Formula:
absolutePrice = oraclePrice * (10000 + offsetBps) / 10000
offsetBps = ((absolutePrice - oraclePrice) * 10000) / oraclePrice
Solidity example
IOROBResolver constant OROB =
IOROBResolver(0x0000000000000000000000000000000000000901);
function limitPrice(int256 oraclePrice) external view returns (int256) {
return OROB.resolveOffset(oraclePrice, -5);
}
viem example
import { createPublicClient, http, parseUnits } from 'viem'
import { hyperpaxeer } from './chains'
const client = createPublicClient({
chain: hyperpaxeer,
transport: http('https://public-rpc.paxeer.app/rpc'),
})
const price = await client.readContract({
address: '0x0000000000000000000000000000000000000901',
abi: [{
type: 'function',
name: 'resolveOffset',
stateMutability: 'view',
inputs: [
{ name: 'oraclePrice', type: 'int256' },
{ name: 'offsetBps', type: 'int16' },
],
outputs: [{ name: 'absolutePrice', type: 'int256' }],
}],
functionName: 'resolveOffset',
args: [parseUnits('3842.50', 18), -5],
})
0x902 BatchClearing
BatchClearing computes the supply-demand crossing for a sealed-bid auction and returns a uniform clearing price.
interface IBatchClearing {
struct ClearingResult {
int16 clearingOffsetBps;
int256 clearingPrice;
uint256 matchedVolume;
}
function computeClearing(
int256 oraclePrice,
int16[] calldata buyOffsets,
uint128[] calldata buySizes,
int16[] calldata sellOffsets,
uint128[] calldata sellSizes
) external view returns (ClearingResult memory result);
}
Input expectations:
buyOffsets are sorted from most aggressive to least aggressive.
sellOffsets are sorted from cheapest to most expensive.
buySizes.length must equal buyOffsets.length.
sellSizes.length must equal sellOffsets.length.
- Empty buy or sell sides return zero matched volume.
IBatchClearing constant CLEARING =
IBatchClearing(0x0000000000000000000000000000000000000902);
function previewBatch(
int256 oraclePrice,
int16[] calldata buyOffsets,
uint128[] calldata buySizes,
int16[] calldata sellOffsets,
uint128[] calldata sellSizes
) external view returns (IBatchClearing.ClearingResult memory) {
return CLEARING.computeClearing(
oraclePrice,
buyOffsets,
buySizes,
sellOffsets,
sellSizes
);
}
0x903 OracleAggregator / VOM
OracleAggregator reads validator-consensus prices from x/paxoracle and lets active validators submit price attestations.
interface IOracleAggregator {
function getValidatorPrice(bytes32 marketId)
external
view
returns (int256 price, uint256 quorum, uint256 timestamp);
function submitPrice(bytes32 marketId, int256 price, uint256 confidence)
external
returns (bool success);
}
Read the validator price
IOracleAggregator constant VOM =
IOracleAggregator(0x0000000000000000000000000000000000000903);
function readBtcPrice() external view returns (int256 price, uint256 quorum) {
bytes32 marketId = keccak256("BTC/USD");
(price, quorum,) = VOM.getValidatorPrice(marketId);
}
Submit a validator price
submitPrice() is for active validators. Transactions from non-validator addresses are rejected by the oracle module.
IOracleAggregator constant VOM =
IOracleAggregator(0x0000000000000000000000000000000000000903);
function submitBtcPrice(int256 price) external returns (bool) {
bytes32 marketId = keccak256("BTC/USD");
uint256 confidence = 1e18;
return VOM.submitPrice(marketId, price, confidence);
}
import { createWalletClient, http, keccak256, parseUnits, stringToBytes } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { hyperpaxeer } from './chains'
const account = privateKeyToAccount(process.env.VALIDATOR_EVM_PRIVATE_KEY as `0x${string}`)
const wallet = createWalletClient({
account,
chain: hyperpaxeer,
transport: http('https://public-rpc.paxeer.app/rpc'),
})
const hash = await wallet.writeContract({
address: '0x0000000000000000000000000000000000000903',
abi: [{
type: 'function',
name: 'submitPrice',
stateMutability: 'nonpayable',
inputs: [
{ name: 'marketId', type: 'bytes32' },
{ name: 'price', type: 'int256' },
{ name: 'confidence', type: 'uint256' },
],
outputs: [{ name: 'success', type: 'bool' }],
}],
functionName: 'submitPrice',
args: [
keccak256(stringToBytes('BTC/USD')),
parseUnits('97250', 18),
parseUnits('1', 18),
],
})
0x904 PoFQScorer
Proof-of-Fill-Quality scores execution quality relative to the oracle price. Scores range from 0 to 1e18, where 1e18 means the fill matched the oracle price.
interface IPoFQScorer {
function scoreFill(int256 fillPrice, int256 oraclePrice)
external
view
returns (uint256 score);
function scoreBatch(
int256[] calldata fillPrices,
int256[] calldata oraclePrices,
uint128[] calldata sizes
) external view returns (uint256 avgScore, uint256 totalVolume);
function updateRollingScore(
uint256 currentScore,
uint256 currentWeight,
uint256 newScore,
uint256 newWeight,
uint16 decayBps
) external view returns (uint256 updatedScore, uint256 updatedWeight);
}
IPoFQScorer constant POFQ =
IPoFQScorer(0x0000000000000000000000000000000000000904);
function score(int256 fillPrice, int256 oraclePrice) external view returns (uint256) {
return POFQ.scoreFill(fillPrice, oraclePrice);
}