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';
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,

View File

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

View File

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

View File

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

View File

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