Building on Openbook
This post has nothing much concrete but works like a small raindrop from my time spent tinkering with Wormhole.
Contents
Some Fundamentals
AMM vs Orderbooks
Liquidity
Building on openbook
Some Fundamentals
Exchanges and markets work by helping two parties who want to trade assets or liabilities, whether that be a simple bazaar or Nasdaq-the basic idea is having a platform connecting two agents who want to trade.
Stock exchanges and physical markets could be easily manipulated by regulation and lobbying, as of today users need to have Brokerage accounts aka middlemen who execute orders on behalf of the customers on the stock exchange.'' This middlemen's inefficiency and censorship led to the development of defi (Decentralised finance) or in layman's terms the finance markets that were censorship-resistant and did not need the intervention of operators.
There are two foundational pillars of modern exchanges:-
AMM(Automated Market Makers)->
Users don't trade with other users but with a liquidity pool of different assets . In AMM models users come in and swap their assets based on a simple model of
Where x is the value of the asset they want to exchange and y is the value of the asset they are exchanging for K is the total value of the pool which should remain constant at the end of the trade(neglecting atomic price changes). Since blockchains are isolated systems, the price of the assets is received from the oracles that provide off-chain data in a verifiable manner that can be accepted on-chain. eg Uniswap, Flash.trade
Orderbooks->
Connecting traders based on their orders is the sole purpose of an order book.
Think of it as a simple matrix that has a bunch of bids of selling and buying an asset and at the center is the price the order is to be executed at.The function of the orderbook is to match these buy and sell orders managing the slippage and price changes along the way. eg Opnbook
Check this talk to dive deeper into how orderbooks work on a higher level
Liquidity
Markets work on the promise of liquidity the promise that if there is someone selling an asset there is a party willing to buy it too, being able to exchange a good for liquid cash or another asset, most of the time this liquidity is provided by whales that have a high amount of liquid assets.
AMMs-> In the Pool-to-peer model, Market Makers become the liquidity providers by staking their tokens in the pool to get some fraction of the fees charged on trades. Pools have different types of risks associated with them based on how the liquidity and leverage are managed.
Order books -> Market Makers have orders on both sides of the order both buy and sell +- fractions of $ fulfilling orders by the normal or retail users.
This is one of the reasons Solana defi suffered so much from FTX crash because the biggest orderbook on solana was Serum built by ftx and Alameda the hedge fund by ftx was the largest amm and liquidity provider literally managing the pillars
As the update authority of the program was still ftx which allowed them access and manipulation of user funds, to prevent any damage the community stepped in and forked serum with the update authority of a multi-sig containing the defi founders.
Check out Article by yash to get more deeper view into Market analysis and Serum.
Building on openbook V2 (lets get our hands dirty)
Let's dive into the core openbook program! Note: we will be using openbook-v1 for the instructions.
wait how does it work?
To build on top of Openbook we use instructions that are built out by the Openbook team by first importing it and then using the instructions so that we don't have to start from scratch, we can use the order book to build new defi primitives on top of it like prep exchanges and simple desks with different liquidate constraints.
//To import any rust packages we use the command
Cargo add serum-dex
// we can replace any but I'm using serum cause its easy lol
This brings the crate in scope and adds it to the cargo.toml file which is important to compile the program.
Now one of the best & worst parts about Solana development→ Each and every library, and program we interact with during deployment has to be listed out which allows the network to parallelize the transactions and makes the compute more efficient.
Here we import all the libraries we might be using in the program.
#![cfg_attr(not(feature = "program"), allow(unused))]
use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
pubkey::Pubkey,
msg,
AccountMeta, Instruction,
pubkey::Pubkey,
sysvar::rent,
};
use crate::error::DexError;
use crate::matching::{OrderType, Side};
use bytemuck::cast;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use arrayref::{array_ref, array_refs};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::num::NonZeroU64;
use serum-dex::*;
pub mod openbook_program {
use solana_program::declare_id;
declare_id!("srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX");
}
Two super important rust crates →
Solana_sdk (Interacting with off chain programs)
solana_program (everything solana on chain)
If you are new to rust you might be wondering what
#![cfg_attr(not(feature = ‘program’),allow(unused)]
is, its a way to suppress warnings with the compiler which halt the program deployment, if you are planning to dive into rust this will become second nature to you
Now we start writing functions and invoking the programs from the openbook instructions, all the actions that a user can take from Limit order to cancel trades to create new swap markets has their own instruction in the core openbook program.
One of the basic ways is to do Cross program invocation, invoking functions from other on-chain programs, targeting a specific instruction on the program. Being able to call functions and programs written by other accounts on chain is what a big part of interoperability is about which means not wasting resources on the foundational infra that every team needs to use. An additional pivotal facet of CPIs is their capacity to empower programs to provide digital signatures for their Program Derived Accounts. In the context of Solana's development landscape, PDAs are heavily employed, granting programs the authority to govern specific addresses in a manner that prevents external users from generating transactions bearing valid signatures for those addresses. The absence of CPIs would drastically diminish the efficacy of PDAs, effectively rendering them inaccessible enclaves—any data deposited into a PDA would be entrapped, unretrievable without the CPI framework.
Like when you use Jupiter, it’s not Jupiter that has the liquidity or access to funds but the contract makes a bunch of CPIs to the different DEXs and orderbooks to find the best route for tokens to maximize fair pricing for the user hence DEX aggregator. being able to access the global state and functions in it allows these protocols to tap into each other’s specific functions.
In rust these functions are either instructions or implications, to check out all the openbook instructions head here . To invoke any program we use
invoke(
&Instruction {
program_id: "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX",
accounts: accounts_meta,
data,
},
&account_infos[account1.clone(), account2.clone(), account3.clone()],
)?;
// specify all the read write accounts before hand
Like if we want to invoke one of the instructions from the openbook that cancels all the open programs
we would create a new variable that uses the order and fills its hyperparameters.
let new_order = NewOrderV3 {
side: openbook::matching::Side::Bid,
limit_price: 100_000, // Example limit price
max_quantity: 1000, // Example max quantity
order_type: openbook::matching::OrderType::Limit,
... // Other fields
};
Python for Openbook
We can use the Pyserum library to interact with the client in python or fetch the price updates.
from pyserum.connection import conn
from pyserum.market import Market
cc = conn("<https://api.mainnet-beta.solana.com/>")
market_address = "openbook adress"
market = Market.load(cc, market_address)
asks = market.load_asks()
print("Ask Orders:")
for ask in asks:
print("Order id: %d, price: %f, size: %f." % (
ask.order_id, ask.info.price, ask.info.size))
print("\\n")
# outpiuts all the current orders
print("Bid Orders:")
bids = market.load_bids()
for bid in bids:
print(f"Order id: {bid.order_id}, price: {bid.info.price}, size: {bid.info.size}.")
PS: This post didn’t have much structure as it was just a braindrop that dived into basic defi fundamentals and into how basic solana development works. Will link up all the resources you need here
openbook DAO voting | Openbook mainnet deployment
Openbook resources