Update program ID, update layouts

This commit is contained in:
Nishad 2020-08-25 06:30:44 +08:00
parent 659896e6e2
commit 3fd28e535b
5 changed files with 151 additions and 25 deletions

View File

@ -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,

View File

@ -27,7 +27,7 @@ describe('instruction', () => {
}, },
}); });
expect(b.toString('hex')).toEqual( expect(b.toString('hex')).toEqual(
'0001000000010000000a00000000000000050000000000000002000000', '0001000000010000000a000000000000000500000000000000020000000000000000000000',
); );
}); });
}); });

View File

@ -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';
} }

View File

@ -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(

View File

@ -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;
} }