@dydxprotocol/v4-client-js 0.38.0 → 0.38.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/__native__/__ios__/v4-native-client.js +2 -2
  2. package/__native__/__ios__/v4-native-client.js.map +1 -1
  3. package/build/examples/composite_example.js +2 -2
  4. package/build/examples/constants.d.ts +1 -0
  5. package/build/examples/constants.js +3 -2
  6. package/build/examples/long_term_order_cancel_example.js +62 -0
  7. package/build/examples/short_term_order_cancel_example.d.ts +1 -0
  8. package/build/examples/short_term_order_cancel_example.js +53 -0
  9. package/build/examples/short_term_order_composite_example.d.ts +1 -0
  10. package/build/examples/{short_term_composite_example.js → short_term_order_composite_example.js} +1 -1
  11. package/build/examples/test.d.ts +1 -0
  12. package/build/examples/test.js +49 -0
  13. package/build/src/clients/composite-client.d.ts +5 -5
  14. package/build/src/clients/composite-client.js +33 -7
  15. package/build/src/clients/modules/get.js +2 -2
  16. package/build/src/lib/utils.d.ts +6 -0
  17. package/build/src/lib/utils.js +11 -2
  18. package/build/src/lib/validation.d.ts +3 -1
  19. package/build/src/lib/validation.js +8 -2
  20. package/examples/composite_example.ts +2 -2
  21. package/examples/constants.ts +2 -0
  22. package/examples/long_term_order_cancel_example.ts +82 -0
  23. package/examples/short_term_order_cancel_example.ts +69 -0
  24. package/examples/test.ts +60 -0
  25. package/package.json +1 -1
  26. package/src/clients/composite-client.ts +39 -8
  27. package/src/clients/modules/get.ts +1 -1
  28. package/src/lib/utils.ts +9 -0
  29. package/src/lib/validation.ts +6 -1
  30. /package/build/examples/{short_term_composite_example.d.ts → long_term_order_cancel_example.d.ts} +0 -0
  31. /package/examples/{short_term_composite_example.ts → short_term_order_composite_example.ts} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  import LocalWallet from '../src/clients/modules/local-wallet';
7
7
  import { Subaccount } from '../src/clients/subaccount';
8
8
  import { randomInt } from '../src/lib/utils';
9
- import { DYDX_TEST_MNEMONIC } from './constants';
9
+ import { DYDX_TEST_MNEMONIC, MAX_CLIENT_ID } from './constants';
10
10
  import ordersParams from './human_readable_orders.json';
11
11
 
12
12
  async function sleep(ms: number): Promise<void> {
@@ -37,7 +37,7 @@ async function test(): Promise<void> {
37
37
  side,
38
38
  price,
39
39
  0.01,
40
- randomInt(100_000_000),
40
+ randomInt(MAX_CLIENT_ID),
41
41
  timeInForce,
42
42
  timeInForceSeconds,
43
43
  OrderExecution.DEFAULT,
@@ -13,6 +13,8 @@ export const PERPETUAL_PAIR_BTC_USD: number = 0;
13
13
  const quantums: Long = new Long(1_000_000_000);
14
14
  const subticks: Long = new Long(1_000_000_000);
15
15
 
16
+ export const MAX_CLIENT_ID = 2 ** 32 - 1;
17
+
16
18
  // PlaceOrder variables
17
19
  export const defaultOrder: IPlaceOrder = {
18
20
  clientId: 0,
@@ -0,0 +1,82 @@
1
+ import { BECH32_PREFIX, OrderFlags } from '../src';
2
+ import { CompositeClient } from '../src/clients/composite-client';
3
+ import {
4
+ Network, OrderExecution, OrderSide, OrderTimeInForce, OrderType,
5
+ } from '../src/clients/constants';
6
+ import LocalWallet from '../src/clients/modules/local-wallet';
7
+ import { Subaccount } from '../src/clients/subaccount';
8
+ import { randomInt } from '../src/lib/utils';
9
+ import { DYDX_TEST_MNEMONIC, MAX_CLIENT_ID } from './constants';
10
+
11
+ async function sleep(ms: number): Promise<void> {
12
+ return new Promise((resolve) => setTimeout(resolve, ms));
13
+ }
14
+
15
+ async function test(): Promise<void> {
16
+ const wallet = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC, BECH32_PREFIX);
17
+ console.log(wallet);
18
+ const network = Network.staging();
19
+ const client = await CompositeClient.connect(network);
20
+ console.log('**Client**');
21
+ console.log(client);
22
+ const subaccount = new Subaccount(wallet, 0);
23
+
24
+ /*
25
+ Note this example places a stateful order.
26
+ Programmatic traders should generally not use stateful orders for following reasons:
27
+ - Stateful orders received out of order by validators will fail sequence number validation
28
+ and be dropped.
29
+ - Stateful orders have worse time priority since they are only matched after they are included
30
+ on the block.
31
+ - Stateful order rate limits are more restrictive than Short-Term orders, specifically max 2 per
32
+ block / 20 per 100 blocks.
33
+ - Stateful orders can only be canceled after they’ve been included in a block.
34
+ */
35
+ const longTermOrderClientId = randomInt(MAX_CLIENT_ID);
36
+ try {
37
+ // place a long term order
38
+ const tx = await client.placeOrder(
39
+ subaccount,
40
+ 'ETH-USD',
41
+ OrderType.LIMIT,
42
+ OrderSide.SELL,
43
+ 40000,
44
+ 0.01,
45
+ longTermOrderClientId,
46
+ OrderTimeInForce.GTT,
47
+ 60,
48
+ OrderExecution.DEFAULT,
49
+ false,
50
+ false,
51
+ );
52
+ console.log('**Long Term Order Tx**');
53
+ console.log(tx.hash);
54
+ } catch (error) {
55
+ console.log('**Long Term Order Failed**');
56
+ console.log(error.message);
57
+ }
58
+
59
+ await sleep(5000); // wait for placeOrder to complete
60
+
61
+ try {
62
+ // cancel the long term order
63
+ const tx = await client.cancelOrder(
64
+ subaccount,
65
+ longTermOrderClientId,
66
+ OrderFlags.LONG_TERM,
67
+ 'ETH-USD',
68
+ 0,
69
+ 120,
70
+ );
71
+ console.log('**Cancel Long Term Order Tx**');
72
+ console.log(tx);
73
+ } catch (error) {
74
+ console.log('**Cancel Long Term Order Failed**');
75
+ console.log(error.message);
76
+ }
77
+ }
78
+
79
+ test().then(() => {
80
+ }).catch((error) => {
81
+ console.log(error.message);
82
+ });
@@ -0,0 +1,69 @@
1
+ import { BECH32_PREFIX, OrderFlags, Order_TimeInForce } from '../src';
2
+ import { CompositeClient } from '../src/clients/composite-client';
3
+ import {
4
+ Network, OrderSide,
5
+ } from '../src/clients/constants';
6
+ import LocalWallet from '../src/clients/modules/local-wallet';
7
+ import { Subaccount } from '../src/clients/subaccount';
8
+ import { randomInt, sleep } from '../src/lib/utils';
9
+ import { DYDX_TEST_MNEMONIC, MAX_CLIENT_ID } from './constants';
10
+
11
+ async function test(): Promise<void> {
12
+ const wallet = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC, BECH32_PREFIX);
13
+ console.log(wallet);
14
+ const network = Network.staging();
15
+ const client = await CompositeClient.connect(network);
16
+ console.log('**Client**');
17
+ console.log(client);
18
+ const subaccount = new Subaccount(wallet, 0);
19
+
20
+ const currentBlock = await client.validatorClient.get.latestBlockHeight();
21
+ const nextValidBlockHeight = currentBlock + 1;
22
+ // Note, you can change this to any number between `next_valid_block_height`
23
+ // to `next_valid_block_height + SHORT_BLOCK_WINDOW`
24
+ const goodTilBlock = nextValidBlockHeight + 10;
25
+ const shortTermOrderClientId = randomInt(MAX_CLIENT_ID);
26
+ try {
27
+ // place a short term order
28
+ const tx = await client.placeShortTermOrder(
29
+ subaccount,
30
+ 'ETH-USD',
31
+ OrderSide.SELL,
32
+ 40000,
33
+ 0.01,
34
+ shortTermOrderClientId,
35
+ goodTilBlock,
36
+ Order_TimeInForce.TIME_IN_FORCE_UNSPECIFIED,
37
+ false,
38
+ );
39
+ console.log('**Short Term Order Tx**');
40
+ console.log(tx.hash);
41
+ } catch (error) {
42
+ console.log('**Short Term Order Failed**');
43
+ console.log(error.message);
44
+ }
45
+
46
+ await sleep(5000); // wait for placeOrder to complete
47
+
48
+ try {
49
+ // cancel the short term order
50
+ const tx = await client.cancelOrder(
51
+ subaccount,
52
+ shortTermOrderClientId,
53
+ OrderFlags.SHORT_TERM,
54
+ 'ETH-USD',
55
+ goodTilBlock + 10,
56
+ 0,
57
+ );
58
+ console.log('**Cancel Short Term Order Tx**');
59
+ console.log(tx);
60
+ } catch (error) {
61
+ console.log('**Cancel Short Term Order Failed**');
62
+ console.log(error.message);
63
+ }
64
+ }
65
+
66
+ test().then(() => {
67
+ }).catch((error) => {
68
+ console.log(error.message);
69
+ });
@@ -0,0 +1,60 @@
1
+ import { BECH32_PREFIX } from '../src';
2
+ import { CompositeClient } from '../src/clients/composite-client';
3
+ import {
4
+ Network, OrderExecution, OrderSide, OrderTimeInForce, OrderType,
5
+ } from '../src/clients/constants';
6
+ import LocalWallet from '../src/clients/modules/local-wallet';
7
+ import { Subaccount } from '../src/clients/subaccount';
8
+ import { randomInt } from '../src/lib/utils';
9
+ import { DYDX_TEST_MNEMONIC, MAX_CLIENT_ID } from './constants';
10
+ import ordersParams from './human_readable_orders.json';
11
+
12
+ async function sleep(ms: number): Promise<void> {
13
+ return new Promise((resolve) => setTimeout(resolve, ms));
14
+ }
15
+
16
+ async function test(): Promise<void> {
17
+ const wallet = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC, BECH32_PREFIX);
18
+ console.log(wallet);
19
+ const network = Network.staging();
20
+ const client = await CompositeClient.connect(network);
21
+ console.log('**Client**');
22
+ console.log(client);
23
+ const subaccount = new Subaccount(wallet, 0);
24
+ for (const orderParams of ordersParams) {
25
+ try {
26
+ const type = OrderType[orderParams.type as keyof typeof OrderType];
27
+ const side = OrderSide[orderParams.side as keyof typeof OrderSide];
28
+ const timeInForceString = orderParams.timeInForce ?? 'GTT';
29
+ const timeInForce = OrderTimeInForce[timeInForceString as keyof typeof OrderTimeInForce];
30
+ const price = orderParams.price ?? 1350;
31
+ const timeInForceSeconds = (timeInForce === OrderTimeInForce.GTT) ? 60 : 0;
32
+ const postOnly = orderParams.postOnly ?? false;
33
+ const tx = await client.placeOrder(
34
+ subaccount,
35
+ 'ETH-USD',
36
+ type,
37
+ side,
38
+ price,
39
+ 0.01,
40
+ randomInt(MAX_CLIENT_ID),
41
+ timeInForce,
42
+ timeInForceSeconds,
43
+ OrderExecution.DEFAULT,
44
+ postOnly,
45
+ false,
46
+ );
47
+ console.log('**Order Tx**');
48
+ console.log(tx);
49
+ } catch (error) {
50
+ console.log(error.message);
51
+ }
52
+
53
+ await sleep(5000); // wait for placeOrder to complete
54
+ }
55
+ }
56
+
57
+ test().then(() => {
58
+ }).catch((error) => {
59
+ console.log(error.message);
60
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dydxprotocol/v4-client-js",
3
- "version": "0.38.0",
3
+ "version": "0.38.2",
4
4
  "description": "General client library for the new dYdX system (v4 decentralized)",
5
5
  "main": "build/src/index.js",
6
6
  "scripts": {
@@ -5,6 +5,7 @@ import { Order_ConditionType, Order_TimeInForce } from '@dydxprotocol/v4-proto/s
5
5
  import Long from 'long';
6
6
  import protobuf from 'protobufjs';
7
7
 
8
+ import { isStatefulOrder, verifyOrderFlags } from '../lib/validation';
8
9
  import { OrderFlags } from '../types';
9
10
  import {
10
11
  DYDX_DENOM,
@@ -160,13 +161,13 @@ export class CompositeClient {
160
161
  }
161
162
 
162
163
  /**
163
- * @description Calculate the goodTilBlock value for a SHORT_TERM order
164
+ * @description Validate the goodTilBlock value for a SHORT_TERM order
164
165
  *
165
166
  * @param goodTilBlock Number of blocks from the current block height the order will
166
167
  * be valid for.
167
168
  *
168
- * @throws UnexpectedClientError if a malformed response is returned with no GRPC error
169
- * at any point.
169
+ * @throws UserError if the goodTilBlock value is not valid given latest block height and
170
+ * SHORT_BLOCK_WINDOW.
170
171
  */
171
172
  private async validateGoodTilBlock(goodTilBlock: number): Promise<void> {
172
173
  const height = await this.validatorClient.get.latestBlockHeight();
@@ -238,8 +239,8 @@ export class CompositeClient {
238
239
  price,
239
240
  size,
240
241
  clientId,
241
- timeInForce,
242
242
  goodTilBlock,
243
+ timeInForce,
243
244
  reduceOnly,
244
245
  );
245
246
  msg.then((it) => resolve([it])).catch((err) => {
@@ -497,7 +498,7 @@ export class CompositeClient {
497
498
  * @param subaccount The subaccount to cancel the order from
498
499
  * @param clientId The client id of the order to cancel
499
500
  * @param orderFlags The order flags of the order to cancel
500
- * @param clobPairId The clob pair id of the order to cancel
501
+ * @param marketId The market to cancel the order on
501
502
  * @param goodTilBlock The goodTilBlock of the order to cancel
502
503
  * @param goodTilBlockTime The goodTilBlockTime of the order to cancel
503
504
  *
@@ -509,10 +510,40 @@ export class CompositeClient {
509
510
  subaccount: Subaccount,
510
511
  clientId: number,
511
512
  orderFlags: OrderFlags,
512
- clobPairId: number,
513
- goodTilBlock?: number,
514
- goodTilBlockTime?: number,
513
+ marketId: string,
514
+ goodTilBlock: number,
515
+ goodTilTimeInSeconds: number,
515
516
  ): Promise<BroadcastTxAsyncResponse | BroadcastTxSyncResponse | IndexedTx> {
517
+
518
+ const marketsResponse = await this.indexerClient.markets.getPerpetualMarkets(marketId);
519
+ const market = marketsResponse.markets[marketId];
520
+ const clobPairId = market.clobPairId;
521
+
522
+ if (!verifyOrderFlags(orderFlags)) {
523
+ throw new Error(`Invalid order flags: ${orderFlags}`);
524
+ }
525
+
526
+ let goodTilBlockTime;
527
+ if (isStatefulOrder(orderFlags)) {
528
+ if (goodTilTimeInSeconds === 0) {
529
+ throw new Error('goodTilTimeInSeconds must be set for LONG_TERM or CONDITIONAL order');
530
+ }
531
+ if (goodTilBlock !== 0) {
532
+ throw new Error(
533
+ 'goodTilBlock should be zero since LONG_TERM or CONDITIONAL orders ' +
534
+ 'use goodTilTimeInSeconds instead of goodTilBlock.',
535
+ );
536
+ }
537
+ goodTilBlockTime = this.calculateGoodTilBlockTime(goodTilTimeInSeconds);
538
+ } else {
539
+ if (goodTilBlock === 0) {
540
+ throw new Error('goodTilBlock must be non-zero for SHORT_TERM orders');
541
+ }
542
+ if (goodTilTimeInSeconds !== 0) {
543
+ throw new Error('goodTilTimeInSeconds should be zero since SHORT_TERM orders use goodTilBlock instead of goodTilTimeInSeconds.');
544
+ }
545
+ }
546
+
516
547
  return this.validatorClient.post.cancelOrder(
517
548
  subaccount,
518
549
  clientId,
@@ -420,7 +420,7 @@ export class Get {
420
420
  );
421
421
 
422
422
  const data: Uint8Array = await this.sendQuery(
423
- '/dydxprotocol.bridge.Query/QueryDelayedCompleteBridgeMessages',
423
+ '/dydxprotocol.bridge.Query/DelayedCompleteBridgeMessages',
424
424
  requestData,
425
425
  );
426
426
  return BridgeModule.QueryDelayedCompleteBridgeMessagesResponse.decode(data);
package/src/lib/utils.ts CHANGED
@@ -34,3 +34,12 @@ export function clientIdFromString(
34
34
  // We must coerce this into a 32-bit unsigned integer.
35
35
  return hash + (2 ** 31);
36
36
  }
37
+
38
+ /**
39
+ * Pauses the execution of the program for a specified time.
40
+ * @param ms - The number of milliseconds to pause the program.
41
+ * @returns A promise that resolves after the specified number of milliseconds.
42
+ */
43
+ export async function sleep(ms: number): Promise<void> {
44
+ return new Promise((resolve) => setTimeout(resolve, ms));
45
+ }
@@ -131,7 +131,12 @@ function verifyNumberIsUint32(num: number): boolean {
131
131
  return num >= 0 && num <= MAX_UINT_32;
132
132
  }
133
133
 
134
- function isStatefulOrder(orderFlags: OrderFlags): boolean {
134
+ export function verifyOrderFlags(orderFlags: OrderFlags): boolean {
135
+ return orderFlags === OrderFlags.SHORT_TERM ||
136
+ orderFlags === OrderFlags.LONG_TERM || orderFlags === OrderFlags.CONDITIONAL;
137
+ }
138
+
139
+ export function isStatefulOrder(orderFlags: OrderFlags): boolean {
135
140
  return orderFlags === OrderFlags.LONG_TERM || orderFlags === OrderFlags.CONDITIONAL;
136
141
  }
137
142