Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch order params & resting orders to use token not atoms #38

Merged
merged 23 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 43 additions & 15 deletions client/ts/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,14 @@ export class ManifestClient {
*
* @param payer PublicKey of the trader
* @param mint PublicKey for deposit mint. Must be either the base or quote
* @param amountAtoms Number of atoms to deposit.
* @param amountTokens Number of tokens to deposit.
*
* @returns TransactionInstruction
*/
public depositIx(
payer: PublicKey,
mint: PublicKey,
amountAtoms: number,
amountTokens: number,
): TransactionInstruction {
const vault: PublicKey = getVaultAddress(this.market.address, mint);
const traderTokenAccount: PublicKey = getAssociatedTokenAddressSync(
Expand All @@ -255,6 +255,11 @@ export class ManifestClient {
const is22: boolean =
(mint == this.baseMint.address && this.isBase22) ||
(mint == this.baseMint.address && this.isBase22);
const mintDecimals =
this.market.quoteMint().toBase58() === mint.toBase58()
? this.market.quoteDecimals()
: this.market.baseDecimals();
const amountAtoms = Math.ceil(amountTokens * 10 ** mintDecimals);

return createDepositInstruction(
{
Expand All @@ -280,16 +285,15 @@ export class ManifestClient {
* Withdraw instruction
*
* @param payer PublicKey of the trader
* @param market PublicKey of the market
* @param mint PublicKey for withdraw mint. Must be either the base or quote
* @param amountAtoms Number of atoms to withdraw.
* @param amountTokens Number of tokens to withdraw.
*
* @returns TransactionInstruction
*/
public withdrawIx(
payer: PublicKey,
mint: PublicKey,
amountAtoms: number,
amountTokens: number,
): TransactionInstruction {
const vault: PublicKey = getVaultAddress(this.market.address, mint);
const traderTokenAccount: PublicKey = getAssociatedTokenAddressSync(
Expand All @@ -299,6 +303,11 @@ export class ManifestClient {
const is22: boolean =
(mint == this.baseMint.address && this.isBase22) ||
(mint == this.baseMint.address && this.isBase22);
const mintDecimals =
this.market.quoteMint().toBase58() === mint.toBase58()
? this.market.quoteDecimals()
: this.market.baseDecimals();
const amountAtoms = Math.floor(amountTokens * 10 ** mintDecimals);

return createWithdrawInstruction(
{
Expand Down Expand Up @@ -345,7 +354,7 @@ export class ManifestClient {
params: {
cancels: [],
cancelAll: false,
orders: [toWrapperPlaceOrderParams(params)],
orders: [toWrapperPlaceOrderParams(this.market, params)],
traderIndexHint: null,
},
},
Expand Down Expand Up @@ -462,7 +471,7 @@ export class ManifestClient {
cancels: cancelParams,
cancelAll,
orders: placeParams.map((params: WrapperPlaceOrderParamsExternal) =>
toWrapperPlaceOrderParams(params),
toWrapperPlaceOrderParams(this.market, params),
),
traderIndexHint: null,
},
Expand All @@ -475,34 +484,53 @@ export class ManifestClient {
* Same as the autogenerated WrapperPlaceOrderParams except price here is a number.
*/
export type WrapperPlaceOrderParamsExternal = {
/** Number of base atoms in the order. */
baseAtoms: bignum;
/** Price as float in atoms of quote per atoms of base. */
price: number;
/** Number of base tokens in the order. */
numBaseTokens: number;
/** Price as float in quote tokens per base tokens. */
tokenPrice: number;
/** Boolean for whether this order is on the bid side. */
isBid: boolean;
/** Last slot before this order is invalid and will be removed. */
lastValidSlot: number;
/** Type of order (Limit, PostOnly, ...). */
orderType: OrderType;
/** Used in fill or kill orders. Set to zero otherwise. */
minOutAtoms?: bignum;
minOutTokens?: number;
/** Client order id used for cancelling orders. Does not need to be unique. */
clientOrderId: bignum;
clientOrderId: number;
DonDuala marked this conversation as resolved.
Show resolved Hide resolved
};

function toWrapperPlaceOrderParams(
market: Market,
wrapperPlaceOrderParamsExternal: WrapperPlaceOrderParamsExternal,
): WrapperPlaceOrderParams {
const quoteAtomsPerToken = 10 ** market.quoteDecimals();
const baseAtomsPerToken = 10 ** market.baseDecimals();
// Converts token price to atom price since not always equal
// Ex. BONK/USDC = 0.00001854 USDC tokens/BONK tokens -> 0.0001854 USDC Atoms/BONK Atoms
const priceQuoteAtomsPerBaseAtoms =
wrapperPlaceOrderParamsExternal.tokenPrice *
(quoteAtomsPerToken / baseAtomsPerToken);
// TODO: Make a helper and test it for this logic.
DonDuala marked this conversation as resolved.
Show resolved Hide resolved
const { priceMantissa, priceExponent } = toMantissaAndExponent(
wrapperPlaceOrderParamsExternal.price,
priceQuoteAtomsPerBaseAtoms,
);
const numBaseAtoms: bignum = Math.floor(
wrapperPlaceOrderParamsExternal.numBaseTokens * baseAtomsPerToken,
);

const minOutTokens = wrapperPlaceOrderParamsExternal.minOutTokens ?? 0;

const minOutAtoms = wrapperPlaceOrderParamsExternal.isBid
? Math.floor(minOutTokens * baseAtomsPerToken)
: Math.floor(minOutTokens * quoteAtomsPerToken);

return {
...wrapperPlaceOrderParamsExternal,
baseAtoms: numBaseAtoms,
priceMantissa,
priceExponent,
minOutAtoms: wrapperPlaceOrderParamsExternal.minOutAtoms ?? 0,
minOutAtoms,
};
}

Expand Down
60 changes: 48 additions & 12 deletions client/ts/src/market.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ export type RestingOrderInternal = {
export type RestingOrder = {
/** Trader public key. */
trader: PublicKey;
/** Number of base atoms remaining in the order. */
numBaseAtoms: bignum;
/** Number of base tokens remaining in the order. */
numBaseTokens: bignum;
/** Last slot before this order is invalid and will be removed. */
lastValidSlot: bignum;
/** Exchange defined sequenceNumber for this order, guaranteed to be unique. */
sequenceNumber: bignum;
/** Price as float in atoms of quote per atoms of base. */
price: number;
/** Price as float in tokens of quote per tokens of base. */
tokenPrice: number;
};

/**
Expand Down Expand Up @@ -157,7 +157,7 @@ export class Market {
}

/**
* Get the amount in atoms of balance that is deposited on the exchange, does
* Get the amount in atoms of balance that is deposited on this market, does
* not include tokens currently in open orders.
*
* @param trader PublicKey of the trader to check balance of
Expand All @@ -180,6 +180,32 @@ export class Market {
return toNum(isBase ? seat.baseBalance : seat.quoteBalance);
}

/**
* Get the amount in tokens of balance that is deposited on this market, does
* not include tokens currently in open orders.
DonDuala marked this conversation as resolved.
Show resolved Hide resolved
*
* @param trader PublicKey of the trader to check balance of
* @param isBase boolean for whether this is checking base or quote
*
* @returns number in tokens
*/
public getWithdrawableBalanceTokens(
trader: PublicKey,
isBase: boolean,
): number {
const filteredSeats = this.data.claimedSeats.filter((claimedSeat) => {
return claimedSeat.publicKey.toBase58() == trader.toBase58();
});
// No seat claimed.
if (filteredSeats.length == 0) {
return 0;
}
const seat: ClaimedSeat = filteredSeats[0];
return isBase
? toNum(seat.baseBalance) / 10 ** this.baseDecimals()
: toNum(seat.quoteBalance) / 10 ** this.quoteDecimals();
}

/**
* Gets the base mint of the market
*
Expand Down Expand Up @@ -260,7 +286,7 @@ export class Market {
/**
* Print all information loaded about the market in a human readable format.
*/
public prettyPrint() {
public prettyPrint(): void {
console.log('');
console.log(`Market: ${this.address}`);
console.log(`========================`);
Expand All @@ -272,13 +298,13 @@ export class Market {
console.log('Bids:');
this.data.bids.forEach((bid) => {
console.log(
`trader: ${bid.trader} numBaseAtoms: ${bid.numBaseAtoms} price: ${bid.price} lastValidSlot: ${bid.lastValidSlot} sequenceNumber: ${bid.sequenceNumber}`,
`trader: ${bid.trader} numBaseTokens: ${bid.numBaseTokens} token price: ${bid.tokenPrice} lastValidSlot: ${bid.lastValidSlot} sequenceNumber: ${bid.sequenceNumber}`,
);
});
console.log('Asks:');
this.data.asks.forEach((ask) => {
console.log(
`trader: ${ask.trader} numBaseAtoms: ${ask.numBaseAtoms} price: ${ask.price} lastValidSlot: ${ask.lastValidSlot} sequenceNumber: ${ask.sequenceNumber}`,
`trader: ${ask.trader} numBaseTokens: ${ask.numBaseTokens} token price: ${ask.tokenPrice} lastValidSlot: ${ask.lastValidSlot} sequenceNumber: ${ask.sequenceNumber}`,
);
});
console.log('ClaimedSeats:');
Expand Down Expand Up @@ -359,7 +385,6 @@ export class Market {
restingOrderBeet,
).map((restingOrderInternal: RestingOrderInternal) => {
return {
...restingOrderInternal,
trader: publicKeyBeet.deserialize(
data.subarray(
Number(restingOrderInternal.traderIndex) +
Expand All @@ -370,7 +395,13 @@ export class Market {
FIXED_MANIFEST_HEADER_SIZE,
),
)[0].publicKey,
price: convertU128(restingOrderInternal.price),
numBaseTokens:
toNum(restingOrderInternal.numBaseAtoms) /
10 ** baseMintDecimals,
tokenPrice:
convertU128(restingOrderInternal.price) *
10 ** (baseMintDecimals - quoteMintDecimals),
...restingOrderInternal,
};
})
: [];
Expand All @@ -383,7 +414,6 @@ export class Market {
restingOrderBeet,
).map((restingOrderInternal: RestingOrderInternal) => {
return {
...restingOrderInternal,
trader: publicKeyBeet.deserialize(
data.subarray(
Number(restingOrderInternal.traderIndex) +
Expand All @@ -394,7 +424,13 @@ export class Market {
FIXED_MANIFEST_HEADER_SIZE,
),
)[0].publicKey,
price: convertU128(restingOrderInternal.price),
numBaseTokens:
toNum(restingOrderInternal.numBaseAtoms) /
10 ** baseMintDecimals,
tokenPrice:
convertU128(restingOrderInternal.price) *
10 ** (baseMintDecimals - quoteMintDecimals),
...restingOrderInternal,
};
})
: [];
Expand Down
29 changes: 13 additions & 16 deletions client/ts/tests/batchUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,12 @@ async function testBatchUpdate(): Promise<void> {
address: marketAddress,
});

await deposit(
connection,
payerKeypair,
marketAddress,
market.baseMint(),
10_000_000_000,
);
await deposit(connection, payerKeypair, marketAddress, market.baseMint(), 10);
await batchUpdate(
connection,
payerKeypair,
marketAddress,
5_000_000_000,
5,
5,
false,
OrderType.Limit,
Expand All @@ -46,23 +40,26 @@ async function testBatchUpdate(): Promise<void> {
// Asks are sorted worst to best.
assert(market.asks().length == 1, 'batch update did not work');
assert(
Number(market.asks()[0].numBaseAtoms) == 5_000_000_000,
Number(market.asks()[0].numBaseTokens) == 5,
'ask top of book wrong size',
);
assert(market.asks()[0].price == 5, 'ask top of book wrong price');
assert(
market.asks()[0].tokenPrice == 5,
`ask top of book wrong price ${market.asks()[0].tokenPrice}`,
);
assert(market.bids().length == 0, 'place bids did not work');
}

async function batchUpdate(
connection: Connection,
payerKeypair: Keypair,
marketAddress: PublicKey,
baseAtoms: number,
price: number,
numBaseTokens: number,
tokenPrice: number,
isBid: boolean,
orderType: OrderType,
clientOrderId: number,
minOutAtoms: number = 0,
minOutTokens: number = 0,
lastValidSlot: number = 0,
): Promise<void> {
const client: ManifestClient = await ManifestClient.getClientForMarket(
Expand All @@ -74,12 +71,12 @@ async function batchUpdate(
const placeOrderIx = client.batchUpdateIx(
[
{
baseAtoms,
price,
numBaseTokens,
tokenPrice,
isBid,
lastValidSlot: lastValidSlot,
orderType: orderType,
minOutAtoms,
minOutTokens,
clientOrderId,
},
],
Expand Down
Loading
Loading