PriceBook¶
The PriceBook contract handles price calculations for each market using an arithmetic pricing model.
Pricing Model¶
Sera uses an arithmetic price book where:
| Parameter | Description | Type |
|---|---|---|
minPrice | Minimum supported price | uint128 |
tickSpace | Price increment per index | uint128 |
priceIndex | Index from 0 to 65,535 | uint16 |
Example¶
For an EURC/USDC market with:
minPrice = 0.90(90 cents per EURC)tickSpace = 0.0001(0.01 cent increments)
| Price Index | Actual Price |
|---|---|
| 0 | 0.9000 |
| 500 | 0.9500 |
| 1000 | 1.0000 |
| 1500 | 1.0500 |
View Functions¶
indexToPrice¶
Converts a price index to the actual price.
Parameters:
priceIndex- The price book index (0-65535)
Returns:
uint256- The actual price in quote token units per base token
Example:
const price = await priceBook.indexToPrice(6500);
console.log(`Price at index 6500: ${ethers.formatUnits(price, 18)}`);
priceToIndex¶
Converts a price to the nearest valid price index.
function priceToIndex(
uint256 price,
bool roundingUp
) external view returns (uint16 index, uint256 correctedPrice)
Parameters:
price- The target priceroundingUp- Iftrue, round up to next index; iffalse, round down
Returns:
index- The nearest valid price indexcorrectedPrice- The actual price at that index
Tip
Use roundingUp = false for bids (buy low) and roundingUp = true for asks (sell high).
Example:
const targetPrice = ethers.parseUnits("0.6533", 18);
// For a bid - round down to ensure we don't overpay
const { index: bidIndex, correctedPrice: bidPrice } =
await priceBook.priceToIndex(targetPrice, false);
// For an ask - round up to ensure we get at least target price
const { index: askIndex, correctedPrice: askPrice } =
await priceBook.priceToIndex(targetPrice, true);
console.log(`Bid index: ${bidIndex}, actual price: ${ethers.formatUnits(bidPrice, 18)}`);
console.log(`Ask index: ${askIndex}, actual price: ${ethers.formatUnits(askPrice, 18)}`);
maxPriceIndex¶
Returns the maximum supported price index.
Returns:
uint16- Always65535(type(uint16).max)
priceUpperBound¶
Returns the maximum supported price.
Returns:
uint256-minPrice + (65535 × tickSpace)
minPrice¶
Returns the minimum price (price at index 0).
tickSpace¶
Returns the price increment per index.
Price Calculation Examples¶
JavaScript Helper¶
class PriceCalculator {
constructor(minPrice, tickSpace) {
this.minPrice = BigInt(minPrice);
this.tickSpace = BigInt(tickSpace);
}
indexToPrice(priceIndex) {
return this.minPrice + this.tickSpace * BigInt(priceIndex);
}
priceToIndex(price, roundingUp = false) {
const priceBN = BigInt(price);
if (priceBN < this.minPrice) {
throw new Error("Price below minimum");
}
const diff = priceBN - this.minPrice;
let index = diff / this.tickSpace;
if (roundingUp && diff % this.tickSpace > 0n) {
index += 1n;
}
if (index > 65535n) {
throw new Error("Price exceeds maximum");
}
return {
index: Number(index),
correctedPrice: this.indexToPrice(Number(index))
};
}
}
// Usage
const calc = new PriceCalculator(
"900000000000000000", // 0.9 with 18 decimals
"100000000000000" // 0.0001 with 18 decimals
);
console.log(calc.indexToPrice(1000)); // 1.0 with 18 decimals
console.log(calc.priceToIndex("950000000000000000")); // { index: 500, ... }
Calculating Spread¶
async function getSpread(orderBook, priceBook) {
const isEmpty = {
bid: await orderBook.isEmpty(true),
ask: await orderBook.isEmpty(false)
};
if (isEmpty.bid || isEmpty.ask) {
return null;
}
const bestBidIndex = await orderBook.bestPriceIndex(true);
const bestAskIndex = await orderBook.bestPriceIndex(false);
const bestBid = await priceBook.indexToPrice(bestBidIndex);
const bestAsk = await priceBook.indexToPrice(bestAskIndex);
const spreadTicks = bestAskIndex - bestBidIndex;
const spreadBps = (bestAsk - bestBid) * 10000n / bestBid;
return {
bidIndex: bestBidIndex,
askIndex: bestAskIndex,
bidPrice: bestBid,
askPrice: bestAsk,
spreadTicks,
spreadBps: Number(spreadBps) / 100 // Convert to percentage
};
}
Error Handling¶
The PriceBook will revert with INVALID_PRICE if:
- Price is below
minPrice - Price is above
priceUpperBound - Rounding up would exceed
maxPriceIndex