Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Core Classes

The vifdk provides several core classes for working with tokens, markets, offers, and order books.

Token

The Token class represents an ERC20 token with Vif-specific unit configuration.

Creating Tokens

import { Token } from "vifdk";
 
const USDC = Token.from(
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // address
  6, // decimals
  "USDC", // symbol
  1n, // unit (optional, default 1n)
);

Units

Units define the precision level for amounts stored in Vif. Amounts are floored to the nearest unit:

// Set custom unit (e.g., 0.0001 USDC precision)
const USDC2 = Token.from(address, 6, "USDC", 10_000n);
 
// Create new token with different unit
const USDC_HIGHER_PRECISION = USDC.withUnit(100n);

Native Token

Token.NATIVE_TOKEN; // ETH with 18 decimals, 1 wei unit

Override if needed:

import { zeroAddress } from "viem";
Token.NATIVE_TOKEN = Token.from(zeroAddress, 18, "ETH", 1n);

Provision Token

Token.PROVISION_TOKEN; // Native token with gwei unit (10^9)

TokenAmount

Represents an amount of a specific token with automatic unit flooring.

Creating Amounts

// From string (parsed using token decimals)
const amount1 = USDC.amount("1000.5");
// amount1.amount === 1000500000n
 
// From bigint (raw amount)
const amount2 = USDC.amount(1000500000n);
 
// Both methods auto-floor to nearest unit
const USDC_UNITS = Token.from(address, 6, "USDC", 10_000n);
const amount3 = USDC_UNITS.amount("1000.50001");
// Floored to 1000.5000 (removed the last digit)

Properties and Methods

const amount = USDC.amount("1000.5");
 
// Get raw amount (bigint)
amount.amount; // 1000500000n
 
// Set raw amount
amount.amount = 2000000000n;
 
// Get normalized amount (in units)
amount.normalizedAmount; // amount / token.unit
 
// Get formatted string
amount.amountString; // '1000.5'
 
// Set from string
amount.amountString = "2000";
 
// Access token
amount.token; // Token instance
 
// Copy amount
const copy = amount.copy();

Market

The Market class represents a trading pair with both directions (asks and bids).

Creating Markets

import { Market } from "vifdk";
const WETH = Token.from(WETH_ADDRESS, 18, "WETH", 1n);
 
const market = Market.create({
  base: WETH, // Or TokenAmount for min offer size
  quote: USDC, // Or TokenAmount for min offer size
  tickSpacing: 1n,
  askFees: 3000, // Optional: 0.3% (in parts per million)
  bidsFees: 3000, // Optional: 0.3% (in parts per million)
});

Market Directions

// Asks: Makers sell WETH for USDC (buyers get WETH)
market.asks;
 
// Bids: Makers buy WETH with USDC (sellers give WETH)
market.bids;

Market Keys

Each direction has a unique identifier:

market.asksKey; // keccak256(base, baseUnit, quote, quoteUnit, tickSpacing)
market.bidsKey; // keccak256(quote, quoteUnit, base, baseUnit, tickSpacing)

Price Conversion

Convert between price and tick:

// Price to tick (for asks: USDC per WETH)
const tick = market.asks.price(3500); // 3500 USDC/WETH
// tick.value is the int24 tick value
 
// Tick to price
const price = Number(tick.price) / 2 ** 128;

Fee Calculations

// Calculate fees on an amount
const feeAmount = USDC.amount("1000");
const fees = market.asks.computeFees(feeAmount);
// Returns TokenAmount with fee
 
// Get amount excluding fees
const amountWithFees = USDC.amount("1000");
const amountExcludingFees = market.asks.excludingFees(amountWithFees);
// Inverse of computeFees

From Open Markets

Create markets from VifReader results:

import { openMarkets, Market as MarketClass } from "vifdk";
 
const openMarketsResult = await client.readContract({
  address: VIF_READER_ADDRESS,
  ...openMarkets(),
});
 
// Convert to Market instances
const DAI = Token.from(DAI_ADDRESS, 18, "DAI", 1n);
const WETH2 = Token.from(WETH_ADDRESS, 18, "WETH", 1n);
const marketInstances = openMarketsResult
  .map((m) => MarketClass.fromOpenMarketResult(m, [WETH2, USDC, DAI]))
  .filter((m) => m !== undefined);

SemiMarket

Represents one direction of a market (either asks or bids).

const asks = market.asks;
 
asks.outboundToken; // Token makers are selling (WETH for asks)
asks.inboundToken; // Token makers are buying (USDC for asks)
asks.market.tickSpacing;
asks.market.base;
asks.market.quote;

Tick

Represents a price level in the order book.

import { Tick } from "vifdk";
 
// From tick value
const tick1 = Tick.fromValue(10000n, 1n);
 
// From price (estimate)
const tick2 = Tick.fromPrice(1.0001, true, 1n);
 
// Get price from tick
const tickPrice = tick1.price; // Fixed point 128.128 number
 
// Convert outbound to inbound
const outbound = WETH.amount("1");
const inbound = tick1.inboundFromOutbound(outbound, USDC);

Offer

Represents a limit order in the book.

import { Offer, rawOffer, offerOwner } from "vifdk";
 
const offerId = 42;
 
// Query the offer
const packedOffer = await client.readContract({
  address: "0x0000000000000000000000000000000000000000" as Address,
  ...rawOffer(market.asks, offerId),
});
 
// Get the owner
const ownerBigInt = await client.readContract({
  address: "0x0000000000000000000000000000000000000000" as Address,
  ...offerOwner(market.asks, offerId),
});
 
const owner = `0x${ownerBigInt.toString(16).padStart(40, "0")}` as Address;
 
// Create Offer instance
const offer = Offer.fromPacked(market.asks, packedOffer, offerId, owner);
 
// Access offer data
offer.data.gives; // TokenAmount
offer.data.received; // TokenAmount
offer.data.tick; // Tick
offer.owner; // Address

OfferList

Represents a list of offers from the order book.

import { OfferList, packedOfferList } from "vifdk";
 
const [nextOfferId, offerIds, packedOffers, owners] =
  await client.readContract({
    address: VIF_READER_ADDRESS,
    ...packedOfferList(market.asks, 0, 100),
  });
 
const offerList = OfferList.fromPacked(
  market.asks,
  offerIds,
  packedOffers,
  owners,
);
 
// Iterate offers
for (const o of offerList.offers) {
  console.log(o.data.gives.amountString);
}
 
// Simulate order
const result = offerList.simulateOrder({
  amount: WETH.amount("1"),
  maxTick: market.asks.price(3600),
});
console.log("Got:", result.got.amountString);
console.log("Gave:", result.gave.amountString);

Book

Represents aggregated price levels from the order book.

import { Book, packedBook } from "vifdk";
 
const [, packedElements] = await client.readContract({
  address: VIF_READER_ADDRESS,
  ...packedBook(market.asks, 0, 100),
});
 
const book = Book.fromPacked(market.asks, packedElements);
 
// View price levels
for (const element of book.elements) {
  const elementPrice = Number(element.tick.price) / 2 ** 128;
  console.log("Price:", elementPrice);
  console.log("Total:", element.totalGives.amountString);
  console.log("Count:", element.offerCount);
}
 
// Simulate order
const bookResult = book.grossSimulation(WETH.amount("1"));
console.log("Got:", bookResult.got.amountString);

Best Practices

  1. Define tokens once - Create token instances at app startup and reuse them
  2. Use strings for amounts - Avoid floating point precision issues
  3. Choose appropriate units - Balance precision with gas costs
  4. Cache markets - Create Market instances once and share them

Related Concepts