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

Markets & Units

Markets in Vif are the fundamental trading pairs, uniquely identified by their configuration parameters. Understanding how markets work and how to configure token units is essential for optimal protocol usage.

Market Identification

Each market is uniquely identified by a keccak256 hash of five parameters:

struct MarketParams {
  address outboundToken;  // Token makers are selling
  uint64  outboundUnits;  // Unit size for outbound amounts
  address inboundToken;   // Token makers are buying
  uint64  inboundUnits;   // Unit size for inbound amounts
  uint16  tickSpacing;    // Minimum tick distance
}
 
bytes32 marketId = keccak256(abi.encodePacked(
  outboundToken,
  outboundUnits,
  inboundToken,
  inboundUnits,
  tickSpacing
));

Market Directionality

Markets are directional - a WETH/USDC market is different from a USDC/WETH market:

// WETH/USDC Market (asks)
// Makers sell WETH, buy USDC
marketId1 = keccak256(WETH, units1, USDC, units2, spacing);
 
// USDC/WETH Market (bids)
// Makers sell USDC, buy WETH
marketId2 = keccak256(USDC, units2, WETH, units1, spacing);
 
// These are DIFFERENT markets
marketId1 != marketId2
Terminology:
  • Asks market: Makers sell base asset (WETH), buy quote asset (USDC)
  • Bids market: Makers sell quote asset (USDC), buy base asset (WETH)

Creating Markets

Only the protocol owner can create new markets:

function openMarket(
  address outboundToken,
  address inboundToken,
  uint64 outboundUnits,
  uint64 inboundUnits,
  uint16 tickSpacing,
  uint24 fee,
  uint24 minOutboundUnits
) external onlyOwner returns (bytes32 marketId);
Parameters:
  • outboundToken / inboundToken: Token addresses
  • outboundUnits / inboundUnits: Unit sizes (must be powers of 10)
  • tickSpacing: Minimum tick distance between offers
  • fee: Initial fee in basis points
  • minOutboundUnits: Minimum offer size in units

Example: WETH/USDC Pair

// Create asks market (sell WETH for USDC)
bytes32 asksMarket = vif.openMarket(
  WETH,                    // outboundToken
  USDC,                    // inboundToken
  1e13,                    // outboundUnits (0.00001 WETH)
  1e4,                     // inboundUnits (0.0001 USDC)
  1,                       // tickSpacing
  0,                       // fee (0 bps)
  1000                     // minOutboundUnits (0.01 WETH minimum)
);
 
// Create bids market (sell USDC for WETH)
bytes32 bidsMarket = vif.openMarket(
  USDC,                    // outboundToken
  WETH,                    // inboundToken
  1e4,                     // outboundUnits
  1e13,                    // inboundUnits
  1,                       // tickSpacing
  0,                       // fee
  1000                     // minOutboundUnits (0.1 USDC minimum)
);

Token Units

Units are the fundamental precision level for token amounts in Vif. They allow packing amounts into 48 bits for storage efficiency.

Why Units?

Without units, storing a full uint256 amount requires:

  • 32 bytes per amount in storage
  • Extremely expensive for high-volume order books

With units, amounts fit in 6 bytes (48 bits):

  • uint48 can represent up to 281,474,976,710,655 units
  • Storage cost reduced by ~81%

How Units Work

All amounts are floored to the nearest unit:

actualAmount = floor(requestedAmount / units) * units;

Example with USDC (units = 1e4, decimals = 6):

units = 10_000;  // 0.0001 USDC precision
 
// Maker wants to offer 1.000011 USDC
requestedAmount = 1_000_011;  // 1.000011 USDC in 6 decimals
 
// Actual amount stored
actualAmount = (1_000_011 / 10_000) * 10_000
             = 100_001 * 10_000
             = 1_000_010;  // 1.00001 USDC
 
// Lost precision: 1 wei (0.000001 USDC)

Choosing Units

Units should be powers of 10 for readability. Choose based on:

  1. Token decimals
  2. Expected price ranges
  3. Minimum meaningful amounts
General formula:
units = 10^(decimals - precisionDigits)

Where precisionDigits is how many significant digits you want to preserve.

Examples by Token Type

Stablecoins (USDC, USDT, DAI)

// USDC (6 decimals)
units = 1e4;  // 10,000 wei = 0.0001 USDC
// Max offer: 281T units * 1e4 / 1e6 = 2.8 billion USDC
// Min tick move: ~0.0001 USDC on $1000 offer
 
// DAI (18 decimals)
units = 1e16;  // 0.01 DAI
// Max offer: 281T units * 1e16 / 1e18 = 2.8 billion DAI

Major Assets (WETH, WBTC)

// WETH (18 decimals)
units = 1e13;  // 0.00001 WETH
// Max offer: 281T units * 1e13 / 1e18 = 2.8 million WETH
// At $3000/WETH = $8.4 billion max
// Min tick move: ~$0.03 on 1 WETH offer
 
// WBTC (8 decimals)
units = 1e3;   // 0.00001 WBTC
// Max offer: 281T units * 1e3 / 1e8 = 2.8 million WBTC

Low-Value Tokens (PEPE, SHIB)

// PEPE (18 decimals, price ~$0.000001)
units = 1e18;  // 1 whole PEPE
// Max offer: 281 trillion PEPE
// At $0.000001 = $281 million max

High-Value Tokens (Rare NFT fractions)

// Fractionalized NFT (18 decimals, price ~$10,000/unit)
units = 1e10;  // 0.00000001 units
// Max offer: 281T units * 1e10 / 1e18 = 2,814 units
// At $10,000 = $28 million max

Units and Tick Precision

The combination of units and tick spacing determines effective price precision:

// WETH/USDC market
outboundUnits = 1e13;  // 0.00001 WETH
inboundUnits = 1e4;    // 0.0001 USDC
tickSpacing = 1;
 
// Price changes by 0.001% per tick
// On a 1 WETH offer:
// 1 tick = 1 WETH * 0.00001 = 0.00001 WETH = $0.03 (at $3000 WETH)
 
// Effective price grid:
// ..., 2999.97, 3000.00, 3000.03, 3000.06, ...

Minimum Offer Sizes

Markets enforce a minimum outbound amount to prevent dust:

minOutboundUnits = 1000;  // Set during market creation
outboundUnits = 1e13;     // WETH units
 
// Minimum WETH offer:
minOffer = minOutboundUnits * outboundUnits
         = 1000 * 1e13
         = 1e16 wei
         = 0.01 WETH

Market Metadata

Each market stores additional metadata:

struct Market {
  bool isActive;           // Can be toggled by owner
  uint24 fee;              // Protocol fee in basis points
  uint24 minOutboundUnits; // Minimum offer size
  uint256 totalFees;       // Accumulated protocol fees
  // ... other fields
}
Owner can modify:
  • isActive - Pause/unpause trading
  • fee - Adjust protocol fees
  • minOutboundUnits - Change minimum order size
Cannot modify:
  • Token addresses
  • Units
  • Tick spacing

Multi-Market Strategies

Since markets are directional, you often need both directions:

Order Book Display

// Get asks (sell WETH for USDC)
Market asks = {
  outboundToken: WETH,
  inboundToken: USDC,
  outboundUnits: 1e13,
  inboundUnits: 1e4,
  tickSpacing: 1
};
 
// Get bids (sell USDC for WETH)
Market bids = {
  outboundToken: USDC,
  inboundToken: WETH,
  outboundUnits: 1e4,
  inboundUnits: 1e13,
  tickSpacing: 1
};
 
// Display spread:
// Best ask: lowest tick in asks market
// Best bid: lowest tick in bids market (inverted for display)

Market Making

// Place offers on both sides
function provideLiquidity() external {
  bytes memory data = abi.encode(...);
  vif.lock(data);
}
 
function lockCallback(bytes calldata) external returns (bytes memory) {
  // Sell WETH at higher price (ask)
  vif.make(maker, asksMarket, 0, 1 ether, askTick, 0, 0);
 
  // Sell USDC at lower price (bid)
  vif.make(maker, bidsMarket, 0, 3000e6, bidTick, 0, 0);
 
  // Settle both
  _settle(WETH, 1 ether);
  _settle(USDC, 3000e6);
 
  return "";
}
// Now earning spread on both sides ✓

Gas Considerations

Market parameters affect gas costs:

ParameterImpact on Gas
tickSpacing = 1More price levels, higher matching cost
tickSpacing = 10Fewer price levels, lower matching cost
outboundUnits smallMore precision, but same gas
outboundUnits largeLess precision, same gas

Recommendation: Use larger tick spacing (10-100) for volatile or low-volume pairs to reduce gas costs.

Related Concepts