progress
This commit is contained in:
parent
787a82a9e9
commit
2c6c3c7ef8
|
@ -117,3 +117,5 @@ dist
|
|||
|
||||
.idea/
|
||||
lib/
|
||||
|
||||
secrets.json
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
lib/
|
||||
node_modules/
|
2
shell
2
shell
|
@ -1,3 +1,5 @@
|
|||
const lib = require('./lib/index');
|
||||
const solana = require('@solana/web3.js');
|
||||
const serum = require('@project-serum/serum');
|
||||
|
||||
const SerumApi = lib.exchange.SerumApi;
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import dotenv from "dotenv";
|
||||
import {PublicKey} from "@solana/web3.js";
|
||||
import {Market} from "./exchange/types";
|
||||
|
||||
// use passed port if specified otherwise default to the .env file
|
||||
const PASSED_PORT = process.env.PORT;
|
||||
|
@ -20,4 +18,5 @@ export const RESTART_INTERVAL_SEC = parseInt(
|
|||
export const HARD_CODED_MINTS = process.env.HARD_CODED_MINTS || {};
|
||||
export const DEFAULT_TIMEOUT = 15000;
|
||||
export const NUM_CONNECTIONS = 1;
|
||||
export const SOLANA_URL = process.env.SOLANA_URL || "http://validator-lb.wirelesstable.net";
|
||||
export const SOLANA_URL =
|
||||
process.env.SOLANA_URL || "http://validator-lb.wirelesstable.net";
|
||||
|
|
|
@ -1,22 +1,56 @@
|
|||
import { Connection, PublicKey } from "@solana/web3.js";
|
||||
import { Exchange, Market, SerumMarketInfo } from "./types";
|
||||
import { Account, Connection, PublicKey } from "@solana/web3.js";
|
||||
import { Coin, Exchange, MarketInfo, Pair } from "./types";
|
||||
import * as config from "../config";
|
||||
import { COIN_MINTS, EXCHANGE_ENABLED_MARKETS } from "./config";
|
||||
import { getKeys } from "../utils";
|
||||
import assert from "assert";
|
||||
import { Market } from "@project-serum/serum";
|
||||
|
||||
export class SerumApi {
|
||||
static readonly exchange: Exchange = "serum";
|
||||
private _connections: Connection[];
|
||||
static url = config.SOLANA_URL;
|
||||
readonly exchange: Exchange;
|
||||
readonly marketInfo: { [market: string]: MarketInfo };
|
||||
readonly markets: Pair[];
|
||||
readonly addressMarkets: { [address: string]: Market };
|
||||
readonly marketAddresses: { [market: string]: PublicKey };
|
||||
readonly addressProgramIds: { [address: string]: PublicKey };
|
||||
private _connections: Connection[];
|
||||
private _publicKey: PublicKey;
|
||||
private _privateKey: Array<number>;
|
||||
private _account: Account;
|
||||
private _wsConnection: Connection;
|
||||
|
||||
constructor(
|
||||
exchange: Exchange,
|
||||
conections: Connection[],
|
||||
marketInfo: { [market: string]: SerumMarketInfo },
|
||||
markets: Market[],
|
||||
marketInfo: { [market: string]: MarketInfo },
|
||||
markets: Pair[],
|
||||
marketAddresses: { [market: string]: PublicKey },
|
||||
addressProgramIds: { [address: string]: PublicKey },
|
||||
url: string
|
||||
) {}
|
||||
) {
|
||||
this.exchange = exchange;
|
||||
this._connections = conections;
|
||||
this._privateKey = getKeys([`${this.exchange}_private_key`])[0];
|
||||
this._account = new Account(this._privateKey);
|
||||
this._publicKey = this._account.publicKey;
|
||||
this.marketInfo = marketInfo;
|
||||
this.markets = markets;
|
||||
this.marketAddresses = marketAddresses;
|
||||
this.addressMarkets = Object.assign(
|
||||
{},
|
||||
...Object.entries(marketAddresses).map(([market, address]) => ({
|
||||
[address.toBase58()]: Pair.fromKey(market),
|
||||
}))
|
||||
);
|
||||
this.addressProgramIds = addressProgramIds;
|
||||
this._wsConnection = new Connection(url, "recent");
|
||||
}
|
||||
|
||||
static async create(options: { [optionName: string]: unknown } = {}): Promise<SerumApi> {
|
||||
static async create(
|
||||
options: { [optionName: string]: unknown } = {}
|
||||
): Promise<SerumApi> {
|
||||
const connections: Connection[] = [];
|
||||
for (let i = 0; i < config.NUM_CONNECTIONS; i++) {
|
||||
const url =
|
||||
|
@ -28,16 +62,20 @@ export class SerumApi {
|
|||
connections.push(connection);
|
||||
}
|
||||
const marketAddresses = Object.fromEntries(
|
||||
this.constMarketInfo.map((info) => [info.market.key(), info.address])
|
||||
);
|
||||
const markets = this.constMarketInfo.map((marketInfo) => marketInfo.market);
|
||||
const addressProgramIds = Object.fromEntries(
|
||||
Object.entries(this.constMarketInfo).map(([market, info]) => [
|
||||
info.address.toBase58(),
|
||||
info.programId,
|
||||
EXCHANGE_ENABLED_MARKETS[this.exchange].map((info) => [
|
||||
info.market.key(),
|
||||
info.address,
|
||||
])
|
||||
);
|
||||
const marketInfo: Array<[Market, SerumMarketInfo]> = await Promise.all(
|
||||
const markets = EXCHANGE_ENABLED_MARKETS[this.exchange].map(
|
||||
(marketInfo) => marketInfo.market
|
||||
);
|
||||
const addressProgramIds = Object.fromEntries(
|
||||
Object.entries(
|
||||
EXCHANGE_ENABLED_MARKETS[this.exchange]
|
||||
).map(([market, info]) => [info.address.toBase58(), info.programId])
|
||||
);
|
||||
const marketInfo: Array<[Pair, MarketInfo]> = await Promise.all(
|
||||
markets.map((market) =>
|
||||
this.getMarketInfo(
|
||||
connections[0],
|
||||
|
@ -60,4 +98,62 @@ export class SerumApi {
|
|||
this.url
|
||||
);
|
||||
}
|
||||
|
||||
static async getMarketInfo(
|
||||
connection: Connection,
|
||||
coin: Coin,
|
||||
priceCurrency: Coin,
|
||||
marketAddress: PublicKey,
|
||||
programId: PublicKey
|
||||
): Promise<any> {
|
||||
const market = new Pair(coin, priceCurrency);
|
||||
const serumMarket = await Market.load(
|
||||
connection,
|
||||
marketAddress,
|
||||
{},
|
||||
programId
|
||||
);
|
||||
assert(
|
||||
serumMarket.baseMintAddress.toBase58() === COIN_MINTS[coin],
|
||||
`${coin} on ${coin}/${priceCurrency} has wrong mint. Our mint: ${
|
||||
COIN_MINTS[coin]
|
||||
} Serum's mint ${serumMarket.baseMintAddress.toBase58()}`
|
||||
);
|
||||
assert(
|
||||
serumMarket.quoteMintAddress.toBase58() === COIN_MINTS[priceCurrency],
|
||||
`${priceCurrency} on ${coin}/${priceCurrency} has wrong mint. Our mint: ${
|
||||
COIN_MINTS[priceCurrency]
|
||||
} Serum's mint ${serumMarket.quoteMintAddress.toBase58()}`
|
||||
);
|
||||
return [
|
||||
market,
|
||||
{
|
||||
coin: coin,
|
||||
priceCurrency: priceCurrency,
|
||||
address: marketAddress,
|
||||
baseMint: serumMarket.baseMintAddress,
|
||||
quoteMint: serumMarket.quoteMintAddress,
|
||||
minOrderSize: serumMarket.minOrderSize,
|
||||
tickSize: serumMarket.tickSize,
|
||||
programId: programId,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async getMarketInfo(): Promise<{ [k: string]: {[prop: string]: string | number}}> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.marketInfo).map(([market, info]) => [
|
||||
market,
|
||||
{
|
||||
...info,
|
||||
address: info.address.toBase58(),
|
||||
baseMint: info.baseMint.toBase58(),
|
||||
quoteMint: info.quoteMint.toBase58(),
|
||||
programId: info.programId.toBase58(),
|
||||
minOrderSize: info.minOrderSize,
|
||||
tickSize: info.tickSize,
|
||||
},
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { getLayoutVersion, MARKETS, TOKEN_MINTS } from "@project-serum/serum";
|
||||
import { HARD_CODED_MINTS } from "../config";
|
||||
import { Market } from "./types";
|
||||
import { Pair } from "./types";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const MARKET_PARAMS = MARKETS.map((marketInfo) => {
|
||||
const [coin, priceCurrency] = marketInfo.name.split("/");
|
||||
return {
|
||||
address: marketInfo.address,
|
||||
market: new Market(coin, priceCurrency),
|
||||
market: new Pair(coin, priceCurrency),
|
||||
programId: marketInfo.programId,
|
||||
version: getLayoutVersion(marketInfo.programId),
|
||||
};
|
||||
|
@ -14,8 +15,26 @@ export const MARKET_PARAMS = MARKETS.map((marketInfo) => {
|
|||
|
||||
export const HARD_CODED_COINS = new Set(Object.keys(HARD_CODED_MINTS));
|
||||
|
||||
export const COIN_MINTS = Object.fromEntries(
|
||||
TOKEN_MINTS.filter(mint => !(mint.name in HARD_CODED_MINTS))
|
||||
export const COIN_MINTS: { [coin: string]: string } = Object.fromEntries(
|
||||
TOKEN_MINTS.filter((mint) => !(mint.name in HARD_CODED_MINTS))
|
||||
.map((mint) => [mint.name, mint.address.toBase58()])
|
||||
.concat(Object.entries(HARD_CODED_MINTS))
|
||||
);
|
||||
|
||||
export const MINT_COINS: { [mint: string]: string } = Object.assign(
|
||||
{},
|
||||
...Object.entries(COIN_MINTS).map(([coin, mint]) => ({
|
||||
[mint]: coin,
|
||||
}))
|
||||
);
|
||||
|
||||
export const EXCHANGE_ENABLED_MARKETS: {
|
||||
[exchange: string]: {
|
||||
address: PublicKey;
|
||||
market: Pair;
|
||||
programId: PublicKey;
|
||||
version: number;
|
||||
}[];
|
||||
} = {
|
||||
serum: MARKET_PARAMS,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export * from "./api";
|
||||
export * from "./config";
|
||||
export * from "./solana";
|
||||
export * from "./utils";
|
|
@ -5,7 +5,7 @@ import BN from "bn.js";
|
|||
export type Coin = string;
|
||||
export type Exchange = string;
|
||||
|
||||
export class Market {
|
||||
export class Pair {
|
||||
coin;
|
||||
priceCurrency;
|
||||
|
||||
|
@ -19,15 +19,15 @@ export class Market {
|
|||
}
|
||||
|
||||
key(): string {
|
||||
return Market.key(this.coin, this.priceCurrency);
|
||||
return Pair.key(this.coin, this.priceCurrency);
|
||||
}
|
||||
|
||||
static fromKey(key: string): Market {
|
||||
static fromKey(key: string): Pair {
|
||||
const [coin, priceCurrency] = key.split("/");
|
||||
return new Market(coin, priceCurrency);
|
||||
return new Pair(coin, priceCurrency);
|
||||
}
|
||||
|
||||
equals(other: Market): boolean {
|
||||
equals(other: Pair): boolean {
|
||||
return (
|
||||
other.coin === this.coin && other.priceCurrency === this.priceCurrency
|
||||
);
|
||||
|
@ -109,7 +109,7 @@ export interface Fill<T = any> {
|
|||
export interface L2OrderBook {
|
||||
bids: [number, number][];
|
||||
asks: [number, number][];
|
||||
market: Market;
|
||||
market: Pair;
|
||||
validAt: number;
|
||||
receivedAt: number;
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ export interface OwnOrders<T = Order> {
|
|||
[orderId: string]: T;
|
||||
}
|
||||
|
||||
export interface SerumMarketInfo {
|
||||
export interface MarketInfo {
|
||||
address: PublicKey;
|
||||
baseMint: PublicKey;
|
||||
quoteMint: PublicKey;
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import {Account, Connection, PublicKey, SystemProgram, Transaction} from "@solana/web3.js";
|
||||
import {
|
||||
Account,
|
||||
Connection,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
Transaction,
|
||||
} from "@solana/web3.js";
|
||||
import BufferLayout from "buffer-layout";
|
||||
import { TokenInstructions } from "@project-serum/serum";
|
||||
|
||||
|
@ -30,7 +36,6 @@ export const MINT_LAYOUT = BufferLayout.struct([
|
|||
BufferLayout.blob(32, "freezeAuthority"),
|
||||
]);
|
||||
|
||||
|
||||
export function parseMintData(
|
||||
data: Buffer
|
||||
): { mintAuthority: PublicKey; supply: number; decimals: number } {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as utils from "./utils";
|
||||
import * as configs from "./config";
|
||||
import * as exchange from "./exchange/index";
|
||||
|
||||
export { utils, configs };
|
||||
export { utils, configs, exchange };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import express from "express";
|
||||
import {SerumApi} from "./exchange/api";
|
||||
import { SerumApi } from "./exchange/api";
|
||||
import expressAsyncHandler from "express-async-handler";
|
||||
import { logger } from "./utils";
|
||||
|
||||
|
@ -7,9 +7,7 @@ const router = express.Router();
|
|||
let api: SerumApi;
|
||||
|
||||
router.get("/", (req, res, next) => {
|
||||
res.send(
|
||||
"Hello from the Serum rest server!"
|
||||
);
|
||||
res.send("Hello from the Serum rest server!");
|
||||
});
|
||||
|
||||
router.use(
|
||||
|
|
|
@ -71,9 +71,7 @@ class MorganStream {
|
|||
}
|
||||
export const morganStream = new MorganStream();
|
||||
|
||||
export const getKeys = (
|
||||
keys: string[]
|
||||
): string[] => {
|
||||
export const getKeys = (keys: string[]): any[] => {
|
||||
const allSecrets = JSON.parse(readFileSync(SECRETS_FILE, "utf-8"));
|
||||
const secrets: string[] = [];
|
||||
for (const key of keys) {
|
||||
|
|
Loading…
Reference in New Issue