Update program ID, update layouts
This commit is contained in:
parent
659896e6e2
commit
3fd28e535b
|
@ -11,7 +11,7 @@ import { PublicKey, TransactionInstruction } from '@solana/web3.js';
|
||||||
import { TOKEN_PROGRAM_ID } from './token-instructions';
|
import { TOKEN_PROGRAM_ID } from './token-instructions';
|
||||||
|
|
||||||
export const DEX_PROGRAM_ID = new PublicKey(
|
export const DEX_PROGRAM_ID = new PublicKey(
|
||||||
'8FxUAcZc27zBH39TfH1G7qVqDTZn3Bm4ym1qxWnAsdKa',
|
'3v9kjrBLN7Awr9BGC2qmFnWLM1EgMAdNm2rXLQFUcQ2d',
|
||||||
);
|
);
|
||||||
|
|
||||||
export const INSTRUCTION_LAYOUT = new VersionedLayout(
|
export const INSTRUCTION_LAYOUT = new VersionedLayout(
|
||||||
|
@ -36,6 +36,7 @@ INSTRUCTION_LAYOUT.inner.addVariant(
|
||||||
u64('limitPrice'),
|
u64('limitPrice'),
|
||||||
u64('maxQuantity'),
|
u64('maxQuantity'),
|
||||||
orderTypeLayout('orderType'),
|
orderTypeLayout('orderType'),
|
||||||
|
u64('clientId'),
|
||||||
]),
|
]),
|
||||||
'newOrder',
|
'newOrder',
|
||||||
);
|
);
|
||||||
|
@ -52,6 +53,11 @@ INSTRUCTION_LAYOUT.inner.addVariant(
|
||||||
'cancelOrder',
|
'cancelOrder',
|
||||||
);
|
);
|
||||||
INSTRUCTION_LAYOUT.inner.addVariant(5, struct([]), 'settleFunds');
|
INSTRUCTION_LAYOUT.inner.addVariant(5, struct([]), 'settleFunds');
|
||||||
|
INSTRUCTION_LAYOUT.inner.addVariant(
|
||||||
|
6,
|
||||||
|
struct([u64('clientId')]),
|
||||||
|
'cancelOrderByClientId',
|
||||||
|
);
|
||||||
|
|
||||||
export function encodeInstruction(instruction) {
|
export function encodeInstruction(instruction) {
|
||||||
const b = Buffer.alloc(100);
|
const b = Buffer.alloc(100);
|
||||||
|
@ -112,6 +118,7 @@ export class DexInstructions {
|
||||||
limitPrice,
|
limitPrice,
|
||||||
maxQuantity,
|
maxQuantity,
|
||||||
orderType,
|
orderType,
|
||||||
|
clientId,
|
||||||
}) {
|
}) {
|
||||||
return new TransactionInstruction({
|
return new TransactionInstruction({
|
||||||
keys: [
|
keys: [
|
||||||
|
@ -126,7 +133,9 @@ export class DexInstructions {
|
||||||
],
|
],
|
||||||
programId: DEX_PROGRAM_ID,
|
programId: DEX_PROGRAM_ID,
|
||||||
data: encodeInstruction({
|
data: encodeInstruction({
|
||||||
newOrder: { side, limitPrice, maxQuantity, orderType },
|
newOrder: clientId
|
||||||
|
? { side, limitPrice, maxQuantity, orderType, clientId }
|
||||||
|
: { side, limitPrice, maxQuantity, orderType },
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -195,6 +204,27 @@ export class DexInstructions {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static cancelOrderByClientId({
|
||||||
|
market,
|
||||||
|
openOrders,
|
||||||
|
owner,
|
||||||
|
requestQueue,
|
||||||
|
clientId,
|
||||||
|
}) {
|
||||||
|
return new TransactionInstruction({
|
||||||
|
keys: [
|
||||||
|
{ pubkey: market, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: openOrders, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: requestQueue, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: owner, isSigner: true, isWritable: false },
|
||||||
|
],
|
||||||
|
programId: DEX_PROGRAM_ID,
|
||||||
|
data: encodeInstruction({
|
||||||
|
cancelOrderByClientId: { clientId },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static settleFunds({
|
static settleFunds({
|
||||||
market,
|
market,
|
||||||
openOrders,
|
openOrders,
|
||||||
|
|
|
@ -27,7 +27,7 @@ describe('instruction', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(b.toString('hex')).toEqual(
|
expect(b.toString('hex')).toEqual(
|
||||||
'0001000000010000000a00000000000000050000000000000002000000',
|
'0001000000010000000a000000000000000500000000000000020000000000000000000000',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
106
src/market.ts
106
src/market.ts
|
@ -143,11 +143,18 @@ export class Market {
|
||||||
this.loadAsks(connection),
|
this.loadAsks(connection),
|
||||||
this.findOpenOrdersAccountsForOwner(connection, ownerAddress),
|
this.findOpenOrdersAccountsForOwner(connection, ownerAddress),
|
||||||
]);
|
]);
|
||||||
return [...bids, ...asks].filter((order) =>
|
const orders = [...bids, ...asks].filter((order) =>
|
||||||
openOrdersAccounts.some((openOrders) =>
|
openOrdersAccounts.some((openOrders) =>
|
||||||
order.openOrdersAddress.equals(openOrders.address),
|
order.openOrdersAddress.equals(openOrders.address),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
// return orders;
|
||||||
|
return orders.map((order) => ({
|
||||||
|
...order,
|
||||||
|
clientId: openOrdersAccounts.find((openOrders) =>
|
||||||
|
order.openOrdersAddress.equals(openOrders.address),
|
||||||
|
)?.clientIds[order.openOrdersSlot],
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async findBaseTokenAccountsForOwner(
|
async findBaseTokenAccountsForOwner(
|
||||||
|
@ -185,7 +192,15 @@ export class Market {
|
||||||
|
|
||||||
async placeOrder(
|
async placeOrder(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
{ owner, payer, side, price, size, orderType = 'limit' }: OrderParams,
|
{
|
||||||
|
owner,
|
||||||
|
payer,
|
||||||
|
side,
|
||||||
|
price,
|
||||||
|
size,
|
||||||
|
orderType = 'limit',
|
||||||
|
clientId,
|
||||||
|
}: OrderParams,
|
||||||
) {
|
) {
|
||||||
const { transaction, signers } = await this.makePlaceOrderTransaction(
|
const { transaction, signers } = await this.makePlaceOrderTransaction(
|
||||||
connection,
|
connection,
|
||||||
|
@ -196,6 +211,7 @@ export class Market {
|
||||||
price,
|
price,
|
||||||
size,
|
size,
|
||||||
orderType,
|
orderType,
|
||||||
|
clientId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return await this._sendTransaction(connection, transaction, signers);
|
return await this._sendTransaction(connection, transaction, signers);
|
||||||
|
@ -203,7 +219,15 @@ export class Market {
|
||||||
|
|
||||||
async makePlaceOrderTransaction<T extends PublicKey | Account>(
|
async makePlaceOrderTransaction<T extends PublicKey | Account>(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
{ owner, payer, side, price, size, orderType = 'limit' }: OrderParams<T>,
|
{
|
||||||
|
owner,
|
||||||
|
payer,
|
||||||
|
side,
|
||||||
|
price,
|
||||||
|
size,
|
||||||
|
orderType = 'limit',
|
||||||
|
clientId,
|
||||||
|
}: OrderParams<T>,
|
||||||
) {
|
) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const ownerAddress: PublicKey = owner.publicKey ?? owner;
|
const ownerAddress: PublicKey = owner.publicKey ?? owner;
|
||||||
|
@ -248,6 +272,7 @@ export class Market {
|
||||||
limitPrice: this.priceNumberToLots(price),
|
limitPrice: this.priceNumberToLots(price),
|
||||||
maxQuantity: this.baseSizeNumberToLots(size),
|
maxQuantity: this.baseSizeNumberToLots(size),
|
||||||
orderType,
|
orderType,
|
||||||
|
clientId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
return { transaction, signers };
|
return { transaction, signers };
|
||||||
|
@ -273,6 +298,40 @@ export class Market {
|
||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async cancelOrderByClientId(
|
||||||
|
connection: Connection,
|
||||||
|
owner: Account,
|
||||||
|
openOrders: PublicKey,
|
||||||
|
clientId: BN,
|
||||||
|
) {
|
||||||
|
const transaction = await this.makeCancelOrderByClientIdTransaction(
|
||||||
|
connection,
|
||||||
|
owner.publicKey,
|
||||||
|
openOrders,
|
||||||
|
clientId,
|
||||||
|
);
|
||||||
|
return await this._sendTransaction(connection, transaction, [owner]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async makeCancelOrderByClientIdTransaction(
|
||||||
|
connection: Connection,
|
||||||
|
owner: PublicKey,
|
||||||
|
openOrders: PublicKey,
|
||||||
|
clientId: BN,
|
||||||
|
) {
|
||||||
|
const transaction = new Transaction();
|
||||||
|
transaction.add(
|
||||||
|
DexInstructions.cancelOrderByClientId({
|
||||||
|
market: this.address,
|
||||||
|
owner,
|
||||||
|
openOrders,
|
||||||
|
requestQueue: this._decoded.requestQueue,
|
||||||
|
clientId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
async cancelOrder(connection: Connection, owner: Account, order: Order) {
|
async cancelOrder(connection: Connection, owner: Account, order: Order) {
|
||||||
const transaction = await this.makeCancelOrderTransaction(
|
const transaction = await this.makeCancelOrderTransaction(
|
||||||
connection,
|
connection,
|
||||||
|
@ -372,23 +431,33 @@ export class Market {
|
||||||
);
|
);
|
||||||
const events = decodeEventQueue(data, limit);
|
const events = decodeEventQueue(data, limit);
|
||||||
return events
|
return events
|
||||||
.filter((event) => event.eventFlags.fill && event.quantityPaid.gtn(0))
|
.filter(
|
||||||
|
(event) => event.eventFlags.fill && event.nativeQuantityPaid.gtn(0),
|
||||||
|
)
|
||||||
.map((event) =>
|
.map((event) =>
|
||||||
event.eventFlags.bid
|
event.eventFlags.bid
|
||||||
? {
|
? {
|
||||||
...event,
|
...event,
|
||||||
size: this.baseSizeLotsToNumber(event.quantityReleased),
|
size: this.baseSplSizeToNumber(event.nativeQuantityReleased),
|
||||||
price: this.priceLotsToNumber(
|
price: event.nativeQuantityPaid
|
||||||
event.quantityPaid.divRound(event.quantityReleased),
|
.mul(this._quoteSplTokenMultiplier)
|
||||||
|
.divRound(
|
||||||
|
event.nativeQuantityReleased.mul(
|
||||||
|
this._baseSplTokenMultiplier,
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
.toNumber(),
|
||||||
side: 'buy',
|
side: 'buy',
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
...event,
|
...event,
|
||||||
size: this.baseSizeLotsToNumber(event.quantityPaid),
|
size: this.baseSplSizeToNumber(event.nativeQuantityPaid),
|
||||||
price: this.priceLotsToNumber(
|
price: event.nativeQuantityReleased
|
||||||
event.quantityReleased.divRound(event.quantityPaid),
|
.mul(this._quoteSplTokenMultiplier)
|
||||||
),
|
.divRound(
|
||||||
|
event.nativeQuantityPaid.mul(this._baseSplTokenMultiplier),
|
||||||
|
)
|
||||||
|
.toNumber(),
|
||||||
side: 'sell',
|
side: 'sell',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -421,6 +490,14 @@ export class Market {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
baseSplSizeToNumber(size: BN) {
|
||||||
|
return divideBnToNumber(size, this._baseSplTokenMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
quoteSplSizeToNumber(size: BN) {
|
||||||
|
return divideBnToNumber(size, this._quoteSplTokenMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
baseSizeLotsToNumber(size: BN) {
|
baseSizeLotsToNumber(size: BN) {
|
||||||
return divideBnToNumber(
|
return divideBnToNumber(
|
||||||
size.mul(this._decoded.baseLotSize),
|
size.mul(this._decoded.baseLotSize),
|
||||||
|
@ -491,6 +568,7 @@ export interface OrderParams<T = Account> {
|
||||||
price: number;
|
price: number;
|
||||||
size: number;
|
size: number;
|
||||||
orderType?: 'limit' | 'ioc' | 'postOnly';
|
orderType?: 'limit' | 'ioc' | 'postOnly';
|
||||||
|
clientId?: BN;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OPEN_ORDERS_LAYOUT = struct([
|
export const OPEN_ORDERS_LAYOUT = struct([
|
||||||
|
@ -511,6 +589,7 @@ export const OPEN_ORDERS_LAYOUT = struct([
|
||||||
u128('isBidBits'),
|
u128('isBidBits'),
|
||||||
|
|
||||||
seq(u128(), 128, 'orders'),
|
seq(u128(), 128, 'orders'),
|
||||||
|
seq(u64(), 128, 'clientIds'),
|
||||||
|
|
||||||
blob(7),
|
blob(7),
|
||||||
]);
|
]);
|
||||||
|
@ -526,6 +605,7 @@ export class OpenOrders {
|
||||||
quoteTokenTotal!: BN;
|
quoteTokenTotal!: BN;
|
||||||
|
|
||||||
orders!: BN[];
|
orders!: BN[];
|
||||||
|
clientIds!: BN[];
|
||||||
|
|
||||||
constructor(address: PublicKey, decoded) {
|
constructor(address: PublicKey, decoded) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
|
@ -662,12 +742,13 @@ export class Orderbook {
|
||||||
}
|
}
|
||||||
|
|
||||||
*[Symbol.iterator](): Generator<Order> {
|
*[Symbol.iterator](): Generator<Order> {
|
||||||
for (const { key, ownerSlot, owner, quantity } of this.slab) {
|
for (const { key, ownerSlot, owner, quantity, feeTier } of this.slab) {
|
||||||
const price = getPriceFromKey(key);
|
const price = getPriceFromKey(key);
|
||||||
yield {
|
yield {
|
||||||
orderId: key,
|
orderId: key,
|
||||||
openOrdersAddress: owner,
|
openOrdersAddress: owner,
|
||||||
openOrdersSlot: ownerSlot,
|
openOrdersSlot: ownerSlot,
|
||||||
|
feeTier,
|
||||||
price: this.market.priceLotsToNumber(price),
|
price: this.market.priceLotsToNumber(price),
|
||||||
priceLots: price,
|
priceLots: price,
|
||||||
size: this.market.baseSizeLotsToNumber(quantity),
|
size: this.market.baseSizeLotsToNumber(quantity),
|
||||||
|
@ -685,6 +766,7 @@ export interface Order {
|
||||||
price: number;
|
price: number;
|
||||||
priceLots: BN;
|
priceLots: BN;
|
||||||
size: number;
|
size: number;
|
||||||
|
feeTier: number;
|
||||||
sizeLots: BN;
|
sizeLots: BN;
|
||||||
side: 'buy' | 'sell';
|
side: 'buy' | 'sell';
|
||||||
}
|
}
|
||||||
|
|
21
src/queue.ts
21
src/queue.ts
|
@ -31,9 +31,10 @@ REQUEST_FLAGS.addBoolean('ioc');
|
||||||
const REQUEST = struct([
|
const REQUEST = struct([
|
||||||
REQUEST_FLAGS,
|
REQUEST_FLAGS,
|
||||||
u8('openOrdersSlot'),
|
u8('openOrdersSlot'),
|
||||||
blob(6),
|
u8('feeTier'),
|
||||||
|
blob(5),
|
||||||
u64('maxBaseSizeOrCancelId'),
|
u64('maxBaseSizeOrCancelId'),
|
||||||
u64('maxQuoteSize'),
|
u64('nativeQuoteQuantityLocked'),
|
||||||
u128('orderId'),
|
u128('orderId'),
|
||||||
publicKeyLayout('openOrders'),
|
publicKeyLayout('openOrders'),
|
||||||
]);
|
]);
|
||||||
|
@ -46,6 +47,8 @@ const EVENT_QUEUE_HEADER = struct([
|
||||||
zeros(4),
|
zeros(4),
|
||||||
u32('count'),
|
u32('count'),
|
||||||
zeros(4),
|
zeros(4),
|
||||||
|
u32('seqNum'),
|
||||||
|
zeros(4),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const EVENT_FLAGS = bits(u8(), false, 'eventFlags');
|
const EVENT_FLAGS = bits(u8(), false, 'eventFlags');
|
||||||
|
@ -57,9 +60,11 @@ EVENT_FLAGS.addBoolean('maker');
|
||||||
const EVENT = struct([
|
const EVENT = struct([
|
||||||
EVENT_FLAGS,
|
EVENT_FLAGS,
|
||||||
u8('openOrdersSlot'),
|
u8('openOrdersSlot'),
|
||||||
blob(6),
|
u8('feeTier'),
|
||||||
u64('quantityReleased'),
|
blob(5),
|
||||||
u64('quantityPaid'),
|
u64('nativeQuantityReleased'),
|
||||||
|
u64('nativeQuantityPaid'),
|
||||||
|
u64('nativeFeeOrRebate'),
|
||||||
u128('orderId'),
|
u128('orderId'),
|
||||||
publicKeyLayout('openOrders'),
|
publicKeyLayout('openOrders'),
|
||||||
]);
|
]);
|
||||||
|
@ -70,9 +75,11 @@ export interface Event {
|
||||||
orderId: BN;
|
orderId: BN;
|
||||||
openOrders: PublicKey;
|
openOrders: PublicKey;
|
||||||
openOrdersSlot: number;
|
openOrdersSlot: number;
|
||||||
|
feeTier: number;
|
||||||
|
|
||||||
quantityReleased: BN;
|
nativeQuantityReleased: BN;
|
||||||
quantityPaid: BN;
|
nativeQuantityPaid: BN;
|
||||||
|
nativeFeeOrRebate: BN;
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeQueue(
|
function decodeQueue(
|
||||||
|
|
11
src/slab.ts
11
src/slab.ts
|
@ -38,7 +38,8 @@ SLAB_NODE_LAYOUT.addVariant(
|
||||||
2,
|
2,
|
||||||
struct([
|
struct([
|
||||||
u8('ownerSlot'), // Index into OPEN_ORDERS_LAYOUT.orders
|
u8('ownerSlot'), // Index into OPEN_ORDERS_LAYOUT.orders
|
||||||
blob(3),
|
u8('feeTier'),
|
||||||
|
blob(2),
|
||||||
u128('key'), // (price, seqNum)
|
u128('key'), // (price, seqNum)
|
||||||
publicKeyLayout('owner'), // Open orders account
|
publicKeyLayout('owner'), // Open orders account
|
||||||
u64('quantity'), // In units of lot size
|
u64('quantity'), // In units of lot size
|
||||||
|
@ -113,7 +114,13 @@ export class Slab {
|
||||||
|
|
||||||
*items(
|
*items(
|
||||||
descending = false,
|
descending = false,
|
||||||
): Generator<{ ownerSlot: number; key: BN; owner: PublicKey; quantity: BN }> {
|
): Generator<{
|
||||||
|
ownerSlot: number;
|
||||||
|
key: BN;
|
||||||
|
owner: PublicKey;
|
||||||
|
quantity: BN;
|
||||||
|
feeTier: number;
|
||||||
|
}> {
|
||||||
if (this.header.leafCount === 0) {
|
if (this.header.leafCount === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue