Expand description

An orderbook-based fixed term market hosted on the Solana blockchain

Interaction

To interact with the fixed term market, users will initialize a PDA called an MarginUser.

After MarginUser intialization, to place an order you must deposit underlying tokens or Jet fixed term market tickets into your account. This will allow you to use the PlaceOrder instruction, which utilizes the orderbook to match borrowers and lenders.

Lending

To lend tokens, a user will deposit the underlying token into their MarginUser account. Then, they may post orders on the book using the PlaceOrder with a given set of OrderParams.

For example, to lend 1_000_000 tokens at 15% interest in a given market, a lender would specify:

OrderParams {
    /// We want as many tickets as the book will give us
    max_ticket_qty: u64::MAX,
    /// we are lending 1_000_000 tokens
    max_underlying_token_qty: 1_000_000,
    /// use the crate function to generate a limit price
    limit_price: limit_price_from_f32((1.0 / 1.15)),
    /// limit the number of matches to 100
    match_limit: 100,
    /// Do not fail transaction if order crosses the spread
    post_only: false,
    /// If order does not get filled immediately, post remainder to the book
    post_allowed: true,
    /// stake generated tickets automatically, creating `SplitTicket`s
    auto_stake: true,
}

Borrowing

For borrowing, a user has two options. They can buy Jet fixed term market tickets from some market, and deposit them into their MarginUser account. Or, they may use the jet-margin program to place collateralized borrow orders.

In the case of a collateralized order, an TermLoan will be minted to track the debt. A user must repay or face liquidation by the jet-margin program.

Example borrow order, where a borrower wants no more than 10% interest to borrow 100_000_000 tokens

OrderParams {
    /// We want to pay no more than 10%
    max_ticket_qty: 110_000_000,
    /// we only need to borrow 100_000_000 tokens
    max_underlying_token_qty: 100_000_000,
    /// use the crate function to generate a limit price
    limit_price: limit_price_from_f32((1.0 / 1.10)),
    /// limit the number of matches to 100
    match_limit: 100,
    /// Do not fail transaction if order crosses the spread
    post_only: false,
    /// If order does not get filled immediately, post remainder to the book
    post_allowed: true,
    /// borrowers do not stake tickets
    auto_stake: false,
}

Orderbook matching engine

To facilitate the pairing of lenders and borrowers, the program utilizes the agnostic-orderbook crate to create an orderbook. This orderbook allows lenders and borrowers to post orders using underlying tokens, held Jet fixed term market tickets, or, by utilizing jet-margin accounts, a collateralized borrow order in lieu of held funds.

EventQueue operation and Adapters

The orderbook works by matching posted orders and pushing events to an EventQueue to be consumed by a crank operating offchain by sending transactions to Solana.

Some users may want to subscribe to events generated by the orderbook matching. To do this, a user must register with the program an Adapter through their MarginUser account using the RegisterAdapter instruction. This instruction creates an AdapterEventQueue PDA to which all processed orders containing the MarginUser account will be pushed.

Users are responsible for handling the consumption logic for their adapter. To clear events after processing, use the PopAdapterEvents instruction.

Jet Fixed Term Market Tickets

Jet fixed term market tickets are fungible spl tokens that must be staked to claim their underlying value. In order to create tickets, a user must either place a lend order on the orderbook, or exchange the token underlying the fixed term market (in practice, almost never will users do this, as it locks their tokens for at least the tenor of the market).

Ticket kinds and redemption

The program allots for two types of ticket redemption. The ClaimTicket is given when a user stakes directly with the program. As there is no information about the creation of the tickets, a ClaimTicket does not have accounting for principal or interest, and only contains a redemptive value.

Conversely, a SplitTicket contains split principal and interest values. As well as the slot it was minted. To create a SplitTicket, you must configure your OrderParams auto_stake flag to true. This will allow to program to immediately stake your tickets as the match event is processed.

After the tenor has passed, the ticket may be redeemed for the underlying value with the program. Also included are instructions for transferring ownership of a ticket.

Debt and Term Loans

When using a jet-margin account to post a collateralized borrow order, an TermLoan is created to track amounts owed to the program. TermLoans are either repaid manually by the user, or handled by an off-chain liquidator.

Modules

An Anchor generated module, providing a set of structs mirroring the structs deriving Accounts, where each field is a Pubkey. This is useful for specifying accounts for a client.
Program instructions and structs related to authoritative control of the program state Control module for the jet-fixed-term-market program.
An Anchor generated module containing the program’s set of instructions, where each method handler in the #[program] mod is associated with a struct defining the input arguments to the method. These should be used directly, when one wants to serialize Anchor instruction data, for example, when speciying instructions on a client.
Program instructions, methods and structs related to the use of margin accounts with the fixed-term program
Program instructions and structs related to use of the on chain orderbook
Module representing the program.
Program instructions and structs related to the redeemable tickets

Enums

Statics

The static program ID

Functions

Confirms that a given pubkey is equivalent to the program ID
The Anchor codegen exposes a programming model where a user defines a set of methods inside of a #[program] module in a way similar to writing RPC request handlers. The macro then generates a bunch of code wrapping these user defined methods into something that can be executed on Solana.
Returns the program ID