Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Bazaar is a permissionless perpetual futures protocol on Arbitrum: any asset with a Pyth price feed can be listed and traded on a fully on-chain central limit order book, margined in USDC. The contracts are immutable, with no owner, pauser or upgrade key, and every role an exchange keeps for itself (listing, insuring, liquidating, firing stops) is open to anyone.

The protocol is not yet deployed: there is no official deployment on any network, so these pages use placeholder factory addresses until Bazaar’s deployments page lists one.

This site documents the TypeScript tools for building on it. They come in two packages:

PackageWhat it isWho it is forRuns in
@liberta-labs/bazaar-sdkEverything an application needs: markets and accounts, live prices and events, every trading action (direct and gasless), USDC approvals and permits, previews, user-facing error messages, history, insurance, termination and settlement, listings and governanceFrontend developers, people writing trading bots and services, relayersBrowsers and Node 20+
@liberta-labs/bazaar-keeperThe keeper: Bazaar’s permissionless upkeep (liquidations, trigger orders, vault unloads, ADL, price refreshes, terminal settlement, collection), run from your own wallet for the bounties it earns, with the bazaar command-line toolKeeper operators; services that want the keeper’s engine as a libraryNode 20+

The keeper is built on the SDK: the chain client, the ABIs, the price layer, the formulas and the market reader are the same code a frontend uses.

What the SDK Does

  • Reads, batched into as few RPC calls as the node allows: the market list with labels, a market’s whole state at one block (prices, funding, margin, open interest, insurance, ADL, termination, the book), an account’s position, health, orders, withdrawable amount and fees, and a portfolio across every market.
  • Live data: a streamed, composed Pyth price per market with freshness and venue-closed flags, and typed contract events with refreshed snapshots, safe across reorgs.
  • Writes, checked before they are built: each builder validates its inputs the way the contract will, then returns a PreparedCall (target, calldata, ETH value, a one-line summary, the USDC it pulls) for your code to simulate and send.
  • Explanations: a revert decoded to a named error and a sentence for the user, for every custom error the contracts declare, and for the failures wallets raise.

What It Does Not Do

  • It never holds a key. It does not sign or send on its own. A write is a PreparedCall that you simulate and hand to the user’s wallet (or, in a bot, to a viem WalletClient holding the bot’s key). Signatures for gasless calls and USDC permits are requested from an EIP-1193 wallet with eth_signTypedData_v4, so the wallet shows the user what they sign.
  • It does not choose for you whom you serve, which markets you show, or how you present them. The contracts cannot tell who calls them; that decision belongs to whoever puts an interface in front of users (see Bazaar’s integrator guide).
  • It does not run a relayer or an indexer. It builds and verifies gasless requests and reads Bazaar’s subgraph; running those services is yours.
  • It does not re-explain the protocol. Margin, funding, liquidation, ADL and termination are defined in Bazaar’s own docs, which these pages link wherever a rule matters.

A Taste

Connect a browser wallet, read the user’s account on the ETH/USD market, and place a limit order, simulated before the wallet is asked to sign:

const [user] = (await window.ethereum.request({ method: "eth_requestAccounts" })) as Address[];
const bz = await Bazaar.connect({ transport: window.ethereum, factory: FACTORY, prices: { token: pythToken } }); // tokens from your backend

const eth = await bz.markets.getMarket("ETH/USD");
const me = await bz.account.accountSnapshot(eth, user!);
console.log(`position ${formatUnits18(me.position.size)}, margin ratio ${me.health?.marginRatio ?? "-"}`);

const call = await bz.trade.createOrder(eth.address, user!, {
  orderType: OrderType.Limit, isLong: true, size: parseUnits18("0.5"), limitPrice: parseUnits18("2400"),
  expirationBlock: me.block + orders.blocksIn(3600), // one hour of Arbitrum blocks
});
const sim = await bz.simulate(call); // as the user, before anything is signed
if (sim.ok) await bz.send(window.ethereum, call); // the wallet signs and sends
else console.error(errorMessage(sim.error)); // "This order needs $1,240.50 of collateral ..."

FACTORY is the deployment’s factory address, from Bazaar’s deployments page, and pythToken a function of your page that returns a short-lived Pyth Pro token from your backend (see Connecting). Amounts are 18-decimal bigints: parseUnits18("0.5") is half a unit, parseUnits18("2400") is $2,400.

A Map of These Docs

Getting Started

The Keeper: what it does, setting one up, its configuration, jobs and budgets, running it in production, and the keeper as a library.

Frontends & Bots: connecting, markets, accounts, live prices and live updates; sending transactions, collateral and permits, placing and managing orders, previews and errors; gasless trading and smart accounts; history; insurance, termination, listings and governance; building a bot and the low-level building blocks.

Reference: the API index, units and constants, security notes for integrators, and development of the packages themselves.