Merge remote-tracking branch 'origin/main'

This commit is contained in:
Sayantan Karmakar 2022-05-24 17:02:07 +05:30
commit 7c731e35e8
5 changed files with 160 additions and 26 deletions

View File

@ -1 +1,6 @@
[237,214,31,185,214,40,80,44,185,17,94,45,161,148,34,70,225,93,44,43,253,83,4,90,110,4,140,76,88,98,138,230,18,131,244,133,112,212,60,253,197,170,165,124,99,156,49,71,39,93,243,190,36,244,55,90,160,53,109,186,224,51,140,82] [
22, 11, 133, 147, 225, 95, 217, 185, 81, 196, 239, 97, 139, 108, 243, 236, 92,
143, 188, 43, 220, 87, 88, 188, 233, 21, 191, 123, 0, 16, 98, 196, 237, 96,
196, 246, 163, 200, 164, 50, 121, 51, 151, 199, 217, 13, 141, 4, 94, 173, 206,
242, 108, 202, 115, 20, 199, 112, 173, 223, 230, 68, 122, 63
]

View File

@ -14,13 +14,27 @@ import { Coin } from "./coin";
import { ChildProcess, fork } from "child_process"; import { ChildProcess, fork } from "child_process";
import { FileKeypair } from "./fileKeypair"; import { FileKeypair } from "./fileKeypair";
export type MarketArgs = { /**
* @param lotSize
* @param tickSize
* @param feeRate
* @param quoteDustThreshold
*/
export type MarketParams = {
lotSize: number; lotSize: number;
tickSize: number; tickSize: number;
feeRate: number; feeRate: number;
quoteDustThreshold: BN; quoteDustThreshold: BN;
}; };
/**
* @param unref
* @param durationInSecs
* @param orderCount
* @param initialBidSize
* @param baseGeckoSymbol
* @param quoteGeckoSymbol
*/
type MarketMakerOpts = { type MarketMakerOpts = {
unref: boolean; unref: boolean;
durationInSecs: number; durationInSecs: number;
@ -49,6 +63,16 @@ export class Dex {
this.markets = []; this.markets = [];
} }
/**
* Create a `Coin` object to be associated with the `Dex`.
*
* @param symbol The symbol of the coin to create
* @param decimals The decimals of the coin to create
* @param payer The payer `Keypair` to use for the transactions
* @param mintAuthority The mint authority `Keypair` to use for the mint
* @param freezeAuthority The freeze authority `Keypair` to use for the mint
* @returns
*/
public createCoin = async ( public createCoin = async (
symbol: string, symbol: string,
decimals: number, decimals: number,
@ -77,12 +101,25 @@ export class Dex {
return coin; return coin;
}; };
/**
* Fetch one of the `Coin` objects associated with the `Dex` by symbol.
*
* @param symbol The symbol of the coin to fetch
* @returns
*/
public getCoin(symbol: string): Coin | null { public getCoin(symbol: string): Coin | null {
const coin = this.coins.find((coin) => coin.symbol === symbol); const coin = this.coins.find((coin) => coin.symbol === symbol);
return coin ? coin : null; return coin ? coin : null;
} }
/**
* Fetch a `DexMarket` object associated with the `Dex` by the base coin and quote coin.
*
* @param baseCoin The base `Coin` of the market to fetch
* @param quoteCoin The quote `Coin` of the market to fetch
* @returns
*/
public getMarket(baseCoin: Coin, quoteCoin: Coin): DexMarket | null { public getMarket(baseCoin: Coin, quoteCoin: Coin): DexMarket | null {
const dexMarket = this.markets.find( const dexMarket = this.markets.find(
(market) => (market) =>
@ -92,11 +129,20 @@ export class Dex {
return dexMarket ? dexMarket : null; return dexMarket ? dexMarket : null;
} }
/**
* Initialize a `DexMarket` instance associated with the `Dex`.
*
* @param payer The payer `Keypair` to use for the transactions
* @param baseCoin The base `Coin` of the market to create
* @param quoteCoin The quote `Coin` of the market to create
* @param marketParams The parameters required to create the market
* @returns
*/
public async initDexMarket( public async initDexMarket(
payer: Keypair, payer: Keypair,
baseCoin: Coin, baseCoin: Coin,
quoteCoin: Coin, quoteCoin: Coin,
marketArgs: MarketArgs, marketParams: MarketParams,
): Promise<DexMarket> { ): Promise<DexMarket> {
if (this.getMarket(baseCoin, quoteCoin) != null) { if (this.getMarket(baseCoin, quoteCoin) != null) {
throw new Error("Market already exists"); throw new Error("Market already exists");
@ -136,10 +182,10 @@ export class Dex {
let baseLotSize; let baseLotSize;
let quoteLotSize; let quoteLotSize;
if (marketArgs.lotSize > 0) { if (marketParams.lotSize > 0) {
baseLotSize = Math.round(10 ** baseCoin.decimals * marketArgs.lotSize); baseLotSize = Math.round(10 ** baseCoin.decimals * marketParams.lotSize);
quoteLotSize = Math.round( quoteLotSize = Math.round(
10 ** quoteCoin.decimals * marketArgs.lotSize * marketArgs.tickSize, 10 ** quoteCoin.decimals * marketParams.lotSize * marketParams.tickSize,
); );
} else { } else {
throw new Error("Invalid Lot Size"); throw new Error("Invalid Lot Size");
@ -164,8 +210,8 @@ export class Dex {
quoteMint: quoteCoin.mint, quoteMint: quoteCoin.mint,
baseLotSize: new BN(baseLotSize), baseLotSize: new BN(baseLotSize),
quoteLotSize: new BN(quoteLotSize), quoteLotSize: new BN(quoteLotSize),
feeRateBps: marketArgs.feeRate, feeRateBps: marketParams.feeRate,
quoteDustThreshold: marketArgs.quoteDustThreshold, quoteDustThreshold: marketParams.quoteDustThreshold,
vaultSignerNonce: vaultOwnerNonce, vaultSignerNonce: vaultOwnerNonce,
programId: this.address, programId: this.address,
}); });
@ -196,7 +242,14 @@ export class Dex {
return dexMarket; return dexMarket;
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars /**
* Runs a Market Making on a separate node process for `durationInSecs` seconds.
*
* @param market The `DexMarket` to run market maker on
* @param owner The owner `Keypair` to use for the market making.
* @param opts The market making options used.
* @returns
*/
public runMarketMaker( public runMarketMaker(
market: DexMarket, market: DexMarket,
owner: FileKeypair, owner: FileKeypair,
@ -205,10 +258,6 @@ export class Dex {
const child = fork("./src/scripts/marketMaker", null, { const child = fork("./src/scripts/marketMaker", null, {
detached: true, detached: true,
stdio: ["pipe", 0, 0, "ipc"], stdio: ["pipe", 0, 0, "ipc"],
env: {
size: "1",
price: "10",
},
}); });
console.log( console.log(

View File

@ -19,7 +19,7 @@ import {
import { Market as SerumMarket } from "@project-serum/serum"; import { Market as SerumMarket } from "@project-serum/serum";
import { Coin } from "./coin"; import { Coin } from "./coin";
import { getDecimalCount, withAssociatedTokenAccount } from "./utils"; import { getDecimalCount, withAssociatedTokenAccount } from "./utils";
import { TransactionWithSigners } from "./types"; import { OrderType, TransactionWithSigners } from "./types";
const REQUEST_QUEUE_SIZE = 5120 + 12; // https://github.com/mithraiclabs/psyoptions/blob/f0c9f73408a27676e0c7f156f5cae71f73f59c3f/programs/psy_american/src/lib.rs#L1003 const REQUEST_QUEUE_SIZE = 5120 + 12; // https://github.com/mithraiclabs/psyoptions/blob/f0c9f73408a27676e0c7f156f5cae71f73f59c3f/programs/psy_american/src/lib.rs#L1003
const EVENT_QUEUE_SIZE = 262144 + 12; // https://github.com/mithraiclabs/psyoptions-ts/blob/ba1888ea83e634e1c7a8dad820fe67d053cf3f5c/packages/psy-american/src/instructions/initializeSerumMarket.ts#L84 const EVENT_QUEUE_SIZE = 262144 + 12; // https://github.com/mithraiclabs/psyoptions-ts/blob/ba1888ea83e634e1c7a8dad820fe67d053cf3f5c/packages/psy-american/src/instructions/initializeSerumMarket.ts#L84
@ -34,14 +34,14 @@ export interface MarketAccounts {
asks: Keypair; asks: Keypair;
} }
// ssh-test /**
* A wrapper class around `serum-ts`'s `Market` class.
*/
export class DexMarket { export class DexMarket {
public address: PublicKey; public address: PublicKey;
public serumMarket: Market; public serumMarket: Market;
// public marketAccounts: MarketAccounts;
public baseCoin: Coin; public baseCoin: Coin;
public quoteCoin: Coin; public quoteCoin: Coin;
@ -61,6 +61,16 @@ export class DexMarket {
this.marketSymbol = `${baseCoin.symbol}/${quoteCoin.symbol}`; this.marketSymbol = `${baseCoin.symbol}/${quoteCoin.symbol}`;
} }
/**
* Load a `DexMarket` instance from a given market address.
*
* @param connection The `Connection` object to connect to Solana.
* @param programID The address of the `serum-dex` program deployed.
* @param marketAddress The address of the market to load.
* @param baseCoin The base `Coin` object provided by the `Coin` class.
* @param quoteCoin The quote `Coin` object provided by the `Coin` class.
* @returns
*/
static async load( static async load(
connection: Connection, connection: Connection,
programID: PublicKey, programID: PublicKey,
@ -85,6 +95,18 @@ export class DexMarket {
return dexMarket; return dexMarket;
} }
/**
* Create a `Transaction` object for creating the vaults required for a DexMarket.
*
* @param payer The `Keypair` of the account that will pay for the transaction.
* @param vaultOwner The address assigned as the owner of the vault.
* @param baseVault The Token Account that would be used as the base vault.
* @param quoteVault The Token Account that would be used as the quote vault.
* @param baseCoin The base `Coin` object provided by the `Coin` class.
* @param quoteCoin The quote `Coin` object provided by the `Coin` class.
* @param connection The `Connection` object to connect to Solana.
* @returns
*/
static async createMarketVaultsTransaction( static async createMarketVaultsTransaction(
payer: Keypair, payer: Keypair,
vaultOwner: PublicKey, vaultOwner: PublicKey,
@ -128,22 +150,31 @@ export class DexMarket {
return tx; return tx;
} }
/**
* Create a `Transaction` object for creating the accounts required for a DexMarket.
*
* @param accounts The `MarketAccounts` object containing the accounts needed for initializing the market.
* @param payer The `Keypair` object of the account that will pay for the transaction.
* @param connection The `Connection` object to connect to Solana.
* @param programID The address of the `serum-dex` program deployed.
* @returns
*/
static async createMarketAccountsInstructions( static async createMarketAccountsInstructions(
accounts: MarketAccounts, accounts: MarketAccounts,
payer: Keypair, payer: Keypair,
connection: Connection, connection: Connection,
dexProgram: PublicKey, programID: PublicKey,
): Promise<TransactionInstruction[]> { ): Promise<TransactionInstruction[]> {
const { market, requestQueue, eventQueue, bids, asks } = accounts; const { market, requestQueue, eventQueue, bids, asks } = accounts;
const marketIx = SystemProgram.createAccount({ const marketIx = SystemProgram.createAccount({
newAccountPubkey: market.publicKey, newAccountPubkey: market.publicKey,
fromPubkey: payer.publicKey, fromPubkey: payer.publicKey,
space: Market.getLayout(dexProgram).span, space: Market.getLayout(programID).span,
lamports: await connection.getMinimumBalanceForRentExemption( lamports: await connection.getMinimumBalanceForRentExemption(
Market.getLayout(dexProgram).span, Market.getLayout(programID).span,
), ),
programId: dexProgram, programId: programID,
}); });
const requestQueueIx = SystemProgram.createAccount({ const requestQueueIx = SystemProgram.createAccount({
@ -153,7 +184,7 @@ export class DexMarket {
lamports: await connection.getMinimumBalanceForRentExemption( lamports: await connection.getMinimumBalanceForRentExemption(
REQUEST_QUEUE_SIZE, REQUEST_QUEUE_SIZE,
), ),
programId: dexProgram, programId: programID,
}); });
const eventQueueIx = SystemProgram.createAccount({ const eventQueueIx = SystemProgram.createAccount({
@ -163,7 +194,7 @@ export class DexMarket {
lamports: await connection.getMinimumBalanceForRentExemption( lamports: await connection.getMinimumBalanceForRentExemption(
EVENT_QUEUE_SIZE, EVENT_QUEUE_SIZE,
), ),
programId: dexProgram, programId: programID,
}); });
const bidsIx = SystemProgram.createAccount({ const bidsIx = SystemProgram.createAccount({
@ -171,7 +202,7 @@ export class DexMarket {
fromPubkey: payer.publicKey, fromPubkey: payer.publicKey,
space: BIDS_SIZE, space: BIDS_SIZE,
lamports: await connection.getMinimumBalanceForRentExemption(BIDS_SIZE), lamports: await connection.getMinimumBalanceForRentExemption(BIDS_SIZE),
programId: dexProgram, programId: programID,
}); });
const asksIx = SystemProgram.createAccount({ const asksIx = SystemProgram.createAccount({
@ -179,7 +210,7 @@ export class DexMarket {
fromPubkey: payer.publicKey, fromPubkey: payer.publicKey,
space: ASKS_SIZE, space: ASKS_SIZE,
lamports: await connection.getMinimumBalanceForRentExemption(ASKS_SIZE), lamports: await connection.getMinimumBalanceForRentExemption(ASKS_SIZE),
programId: dexProgram, programId: programID,
}); });
return [marketIx, requestQueueIx, eventQueueIx, bidsIx, asksIx]; return [marketIx, requestQueueIx, eventQueueIx, bidsIx, asksIx];
@ -218,11 +249,24 @@ export class DexMarket {
throw new Error(`Price must be greater than ${formattedTickSize}`); throw new Error(`Price must be greater than ${formattedTickSize}`);
} }
/**
* Create a `Transaction` object for placing an order.
*
* @param connection The `Connection` object to connect to Solana.
* @param owner The `PublicKey` of the owner of the order.
* @param serumMarket The `Market` object from `serum-ts` package.
* @param side The `Side` of the order.
* @param orderType The `OrderType` of the order.
* @param size The `size` of the order.
* @param price The `price` of the order.
* @returns
*/
static async getPlaceOrderTransaction( static async getPlaceOrderTransaction(
connection: Connection, connection: Connection,
owner: Keypair, owner: Keypair,
serumMarket: SerumMarket, serumMarket: SerumMarket,
side: "buy" | "sell", side: "buy" | "sell",
orderType: OrderType,
size: number, size: number,
price: number, price: number,
): Promise<TransactionWithSigners> { ): Promise<TransactionWithSigners> {
@ -268,7 +312,7 @@ export class DexMarket {
side, side,
price, price,
size, size,
orderType: "limit", orderType,
feeDiscountPubkey: null, feeDiscountPubkey: null,
openOrdersAddressKey: openOrders.address, openOrdersAddressKey: openOrders.address,
}; };
@ -291,11 +335,24 @@ export class DexMarket {
return { transaction, signers }; return { transaction, signers };
} }
/**
* Place an order on the DexMarket.
*
* @param connection The `Connection` object to connect to Solana.
* @param owner The `PublicKey` of the owner of the order.
* @param serumMarket The `Market` object from `serum-ts` package.
* @param side The `Side` of the order.
* @param orderType The `OrderType` of the order.
* @param size The `size` of the order.
* @param price The `price` of the order.
* @returns
*/
static async placeOrder( static async placeOrder(
connection: Connection, connection: Connection,
owner: Keypair, owner: Keypair,
serumMarket: SerumMarket, serumMarket: SerumMarket,
side: "buy" | "sell", side: "buy" | "sell",
orderType: OrderType,
size: number, size: number,
price: number, price: number,
): Promise<string> { ): Promise<string> {
@ -304,6 +361,7 @@ export class DexMarket {
owner, owner,
serumMarket, serumMarket,
side, side,
orderType,
size, size,
price, price,
); );
@ -314,6 +372,15 @@ export class DexMarket {
return txSig; return txSig;
} }
/**
* Create a `Transaction` object for cancelling an order.
*
* @param connection The `Connection` object to connect to Solana.
* @param owner The `PublicKey` of the owner of the order.
* @param serumMarket The `Market` object from `serum-ts` package.
* @param order The `Order` object to cancel.
* @returns
*/
static async getCancelOrderTransaction( static async getCancelOrderTransaction(
connection: Connection, connection: Connection,
owner: Keypair, owner: Keypair,
@ -332,6 +399,15 @@ export class DexMarket {
}; };
} }
/**
* Cancel an order on the DexMarket.
*
* @param connection The `Connection` object to connect to Solana.
* @param owner The `PublicKey` of the owner of the order.
* @param serumMarket The `Market` object from `serum-ts` package.
* @param order The `Order` object to cancel.
* @returns
*/
static async cancelOrder( static async cancelOrder(
connection: Connection, connection: Connection,
owner: Keypair, owner: Keypair,

View File

@ -118,6 +118,7 @@ const placeOrders = async (
owner, owner,
serumMarket, serumMarket,
"buy", "buy",
"postOnly",
buySize, buySize,
buyPrice, buyPrice,
); );
@ -132,6 +133,7 @@ const placeOrders = async (
owner, owner,
serumMarket, serumMarket,
"sell", "sell",
"postOnly",
sellSize, sellSize,
sellPrice, sellPrice,
); );

View File

@ -4,3 +4,5 @@ export type TransactionWithSigners = {
transaction: Transaction; transaction: Transaction;
signers: Signer[]; signers: Signer[];
}; };
export type OrderType = "limit" | "ioc" | "postOnly";