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

add batch cancel short term orders #228

Merged
merged 2 commits into from
Aug 27, 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
65 changes: 59 additions & 6 deletions v4-client-js/__native__/__ios__/v4-native-client.js

Large diffs are not rendered by default.

130 changes: 130 additions & 0 deletions v4-client-js/examples/batch_cancel_orders_example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import _ from 'lodash';

import { BECH32_PREFIX, Order_TimeInForce } from '../src';
import { CompositeClient, OrderBatchWithMarketId } from '../src/clients/composite-client';
import { Network, OrderSide } from '../src/clients/constants';
import LocalWallet from '../src/clients/modules/local-wallet';
import { SubaccountInfo } from '../src/clients/subaccount';
import { randomInt, sleep } from '../src/lib/utils';
import { DYDX_TEST_MNEMONIC, MAX_CLIENT_ID } from './constants';

type OrderInfo = {
marketId: string;
clientId: number;
side: OrderSide;
price: number;
size: number;
};

const generateShortTermOrdersInfo = (): OrderInfo[] => [
{
marketId: 'ETH-USD',
clientId: randomInt(MAX_CLIENT_ID),
side: OrderSide.SELL,
price: 4000,
size: 0.01,
},
{
marketId: 'ETH-USD',
clientId: randomInt(MAX_CLIENT_ID),
side: OrderSide.SELL,
price: 4200,
size: 0.02,
},
{
marketId: 'BTC-USD',
clientId: randomInt(MAX_CLIENT_ID),
side: OrderSide.BUY,
price: 40000,
size: 0.01,
},
];

const generateBatchCancelShortTermOrders = (ordersInfo: OrderInfo[]): OrderBatchWithMarketId[] => {
const ordersGroupedByMarketIds = _.groupBy(ordersInfo, (info) => info.marketId);
return Object.keys(ordersGroupedByMarketIds).map((marketId) => ({
marketId,
clientIds: ordersGroupedByMarketIds[marketId].map((info) => info.clientId),
}));
};

async function test(): Promise<void> {
try {
const wallet = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC, BECH32_PREFIX);
console.log('**Wallet**', wallet);

const network = Network.testnet();
const client = await CompositeClient.connect(network);
console.log('**Client**', client);

const subaccount = new SubaccountInfo(wallet, 0);
const currentBlock = await client.validatorClient.get.latestBlockHeight();
const goodTilBlock = currentBlock + 10;

const shortTermOrdersInfo = generateShortTermOrdersInfo();
await placeShortTermOrders(client, subaccount, shortTermOrdersInfo, goodTilBlock);
await sleep(5000);
await batchCancelOrders(client, subaccount, shortTermOrdersInfo, goodTilBlock);
} catch (error) {
console.error('**Test Failed**', error.message);
}
}

const placeShortTermOrders = async (
client: CompositeClient,
subaccount: SubaccountInfo,
shortTermOrdersInfo: OrderInfo[],
goodTilBlock: number,
): Promise<void> => {
const orderPromises = shortTermOrdersInfo.map(async (order) => {
try {
const tx = await client.placeShortTermOrder(
subaccount,
order.marketId,
order.side,
order.price,
order.size,
order.clientId,
goodTilBlock,
Order_TimeInForce.TIME_IN_FORCE_UNSPECIFIED,
false,
);
console.log('**Short Term Order Tx**', tx.hash);
} catch (error) {
console.error(
`**Short Term Order Failed for Market ${order.marketId}, Client ID ${order.clientId}**`,
error.message,
);
}
});

// Wait for all order placements to complete
await Promise.all(orderPromises);
}

const batchCancelOrders = async (
client: CompositeClient,
subaccount: SubaccountInfo,
shortTermOrdersInfo: OrderInfo[],
goodTilBlock: number,
): Promise<void> => {
const shortTermOrdersPayload = generateBatchCancelShortTermOrders(shortTermOrdersInfo);
try {
const tx = await client.batchCancelShortTermOrdersWithMarketId(
subaccount,
shortTermOrdersPayload,
goodTilBlock + 10,
);
console.log('**Batch Cancel Short Term Orders Tx**', tx);
} catch (error) {
console.error('**Batch Cancel Short Term Orders Failed**', error.message);
}
}

test()
.then(() => {
console.log('**Batch Cancel Test Completed Successfully**');
})
.catch((error) => {
console.error('**Batch Cancel Test Execution Error**', error.message);
});
4 changes: 2 additions & 2 deletions v4-client-js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion v4-client-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dydxprotocol/v4-client-js",
"version": "1.1.31",
"version": "1.2.0",
"description": "General client library for the new dYdX system (v4 decentralized)",
"main": "build/src/index.js",
"scripts": {
Expand Down
57 changes: 56 additions & 1 deletion v4-client-js/src/clients/composite-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { UserError } from './lib/errors';
import { generateRegistry } from './lib/registry';
import LocalWallet from './modules/local-wallet';
import { SubaccountInfo } from './subaccount';
import { BroadcastMode } from './types';
import { BroadcastMode, OrderBatch } from './types';
import { ValidatorClient } from './validator-client';

// Required for encoding and decoding queries that are of type Long.
Expand All @@ -58,6 +58,12 @@ export interface MarketInfo {
subticksPerTick: number;
}

export interface OrderBatchWithMarketId {
marketId: string;
clobPairId?: number;
clientIds: number[];
}

export class CompositeClient {
public readonly network: Network;
public gasDenom: SelectedGasDenom = SelectedGasDenom.USDC;
Expand Down Expand Up @@ -696,6 +702,55 @@ export class CompositeClient {
);
}

/**
* @description Batch cancel short term orders using marketId to clobPairId translation.
*
* @param subaccount The subaccount to cancel the order from
* @param shortTermOrders The list of short term order batches to cancel with marketId
* @param goodTilBlock The goodTilBlock of the order to cancel
* @returns The transaction hash.
*/
async batchCancelShortTermOrdersWithMarketId(
subaccount: SubaccountInfo,
shortTermOrders: OrderBatchWithMarketId[],
goodTilBlock: number,
): Promise<BroadcastTxAsyncResponse | BroadcastTxSyncResponse | IndexedTx> {
const orderBatches = await Promise.all(
shortTermOrders.map(async ({ marketId, clobPairId, clientIds }) => ({
clobPairId: (
clobPairId ??
(await this.indexerClient.markets.getPerpetualMarkets(marketId)).markets[marketId]
).clobPairId,
clientIds }))
);

return this.validatorClient.post.batchCancelShortTermOrders(
subaccount,
orderBatches,
goodTilBlock,
);
}

/**
* @description Batch cancel short term orders using clobPairId.
*
* @param subaccount The subaccount to cancel the order from
* @param shortTermOrders The list of short term order batches to cancel with clobPairId
* @param goodTilBlock The goodTilBlock of the order to cancel
* @returns The transaction hash.
*/
async batchCancelShortTermOrdersWithClobPairId(
subaccount: SubaccountInfo,
shortTermOrders: OrderBatch[],
goodTilBlock: number,
): Promise<BroadcastTxAsyncResponse | BroadcastTxSyncResponse | IndexedTx> {
return this.validatorClient.post.batchCancelShortTermOrders(
subaccount,
shortTermOrders,
goodTilBlock,
);
}

/**
* @description Transfer from a subaccount to another subaccount
*
Expand Down
3 changes: 2 additions & 1 deletion v4-client-js/src/clients/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export const TYPE_URL_MSG_SUBMIT_PROPOSAL = '/cosmos.gov.v1.MsgSubmitProposal';
// x/clob
export const TYPE_URL_MSG_PLACE_ORDER = '/dydxprotocol.clob.MsgPlaceOrder';
export const TYPE_URL_MSG_CANCEL_ORDER = '/dydxprotocol.clob.MsgCancelOrder';
export const TYPE_URL_BATCH_CANCEL = '/dydxprotocol.clob.MsgBatchCancel';
export const TYPE_URL_MSG_CREATE_CLOB_PAIR = '/dydxprotocol.clob.MsgCreateClobPair';
export const TYPE_URL_MSG_UPDATE_CLOB_PAIR = '/dydxprotocol.clob.MsgUpdateClobPair';

Expand All @@ -101,7 +102,7 @@ export const TYPE_URL_MSG_UNDELEGATE = '/cosmos.staking.v1beta1.MsgUndelegate';
export const TYPE_URL_MSG_WITHDRAW_DELEGATOR_REWARD =
'/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward';

// ------------ Chain Constants ------------
// ------------ Chain Constants ------------
// The following are same across different networks / deployments.
export const GOV_MODULE_ADDRESS = 'dydx10d07y265gmmuvt4z0w9aw880jnsr700jnmapky';
export const DELAYMSG_MODULE_ADDRESS = 'dydx1mkkvp26dngu6n8rmalaxyp3gwkjuzztq5zx6tr';
Expand Down
3 changes: 3 additions & 0 deletions v4-client-js/src/clients/lib/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
MsgCancelOrder,
MsgCreateClobPair,
MsgUpdateClobPair,
MsgBatchCancel,
} from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/clob/tx';
import { MsgDelayMessage } from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/delaymsg/tx';
import { MsgCreatePerpetual } from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/perpetuals/tx';
Expand All @@ -26,6 +27,7 @@ import {
TYPE_URL_MSG_CREATE_TRANSFER,
TYPE_URL_MSG_WITHDRAW_FROM_SUBACCOUNT,
TYPE_URL_MSG_DEPOSIT_TO_SUBACCOUNT,
TYPE_URL_BATCH_CANCEL,
} from '../constants';

export const registry: ReadonlyArray<[string, GeneratedType]> = [];
Expand All @@ -34,6 +36,7 @@ export function generateRegistry(): Registry {
// clob
[TYPE_URL_MSG_PLACE_ORDER, MsgPlaceOrder as GeneratedType],
[TYPE_URL_MSG_CANCEL_ORDER, MsgCancelOrder as GeneratedType],
[TYPE_URL_BATCH_CANCEL, MsgBatchCancel as GeneratedType],
[TYPE_URL_MSG_CREATE_CLOB_PAIR, MsgCreateClobPair as GeneratedType],
[TYPE_URL_MSG_UPDATE_CLOB_PAIR, MsgUpdateClobPair as GeneratedType],

Expand Down
26 changes: 26 additions & 0 deletions v4-client-js/src/clients/modules/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {
} from '@dydxprotocol/v4-proto/src/codegen/cosmos/staking/v1beta1/tx';
import { ClobPair_Status } from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/clob/clob_pair';
import {
MsgBatchCancel,
MsgCreateClobPair,
MsgUpdateClobPair,
OrderBatch,
} from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/clob/tx';
import { MsgDelayMessage } from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/delaymsg/tx';
import { PerpetualMarketType } from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/perpetuals/perpetual';
Expand Down Expand Up @@ -40,6 +42,7 @@ import {
TYPE_URL_MSG_DELEGATE,
TYPE_URL_MSG_UNDELEGATE,
TYPE_URL_MSG_WITHDRAW_DELEGATOR_REWARD,
TYPE_URL_BATCH_CANCEL,
} from '../constants';
import { DenomConfig } from '../types';
import {
Expand Down Expand Up @@ -149,6 +152,29 @@ export class Composer {
};
}

public composeMsgBatchCancelShortTermOrders(
address: string,
subaccountNumber: number,
shortTermCancels: OrderBatch[],
goodTilBlock: number,
): EncodeObject {
const subaccountId: SubaccountId = {
owner: address,
number: subaccountNumber,
};

const msg: MsgBatchCancel = {
subaccountId,
shortTermCancels,
goodTilBlock,
};

return {
typeUrl: TYPE_URL_BATCH_CANCEL,
value: msg,
};
}

public composeMsgCreateClobPair(
clobId: number,
perpetualId: number,
Expand Down
40 changes: 40 additions & 0 deletions v4-client-js/src/clients/modules/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
MsgPlaceOrder,
MsgCancelOrder,
Order_ConditionType,
OrderBatch,
} from './proto-includes';

// Required for encoding and decoding queries that are of type Long.
Expand Down Expand Up @@ -520,6 +521,45 @@ export class Post {
);
}

async batchCancelShortTermOrders(
subaccount: SubaccountInfo,
shortTermOrders: OrderBatch[],
goodTilBlock: number,
broadcastMode?: BroadcastMode,
): Promise<BroadcastTxAsyncResponse | BroadcastTxSyncResponse | IndexedTx> {
const msg = await this.batchCancelShortTermOrdersMsg(
subaccount.address,
subaccount.subaccountNumber,
shortTermOrders,
goodTilBlock,
);
return this.send(
subaccount.wallet,
() => Promise.resolve([msg]),
true,
undefined,
undefined,
broadcastMode,
);
}

async batchCancelShortTermOrdersMsg(
address: string,
subaccountNumber: number,
shortTermOrders: OrderBatch[],
goodTilBlock: number,
): Promise<EncodeObject> {
return new Promise((resolve) => {
const msg = this.composer.composeMsgBatchCancelShortTermOrders(
address,
subaccountNumber,
shortTermOrders,
goodTilBlock,
);
resolve(msg);
});
}

async transfer(
subaccount: SubaccountInfo,
recipientAddress: string,
Expand Down
Loading