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