@dydxprotocol/v4-client-js 0.38.3 → 0.38.5

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.
@@ -1,5 +1,7 @@
1
1
  import { EncodeObject } from '@cosmjs/proto-signing';
2
- import { GasPrice, IndexedTx, StdFee } from '@cosmjs/stargate';
2
+ import {
3
+ Account, GasPrice, IndexedTx, StdFee,
4
+ } from '@cosmjs/stargate';
3
5
  import { BroadcastTxAsyncResponse, BroadcastTxSyncResponse } from '@cosmjs/tendermint-rpc/build/tendermint37';
4
6
  import { Order_ConditionType, Order_TimeInForce } from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/clob/order';
5
7
  import Long from 'long';
@@ -10,7 +12,13 @@ import { OrderFlags } from '../types';
10
12
  import {
11
13
  DYDX_DENOM,
12
14
  GAS_PRICE,
13
- Network, OrderExecution, OrderSide, OrderTimeInForce, OrderType, SHORT_BLOCK_WINDOW,
15
+ Network,
16
+ OrderExecution,
17
+ OrderSide,
18
+ OrderTimeInForce,
19
+ OrderType,
20
+ SHORT_BLOCK_FORWARD,
21
+ SHORT_BLOCK_WINDOW,
14
22
  } from './constants';
15
23
  import {
16
24
  calculateQuantums,
@@ -35,6 +43,14 @@ import { ValidatorClient } from './validator-client';
35
43
  protobuf.util.Long = Long;
36
44
  protobuf.configure();
37
45
 
46
+ export interface MarketInfo {
47
+ clobPairId: number;
48
+ atomicResolution: number;
49
+ stepBaseQuantums: number;
50
+ quantumConversionExponent: number;
51
+ subticksPerTick: number;
52
+ }
53
+
38
54
  export class CompositeClient {
39
55
  public readonly network: Network;
40
56
  private _indexerClient: IndexerClient;
@@ -89,8 +105,16 @@ export class CompositeClient {
89
105
  zeroFee: boolean,
90
106
  gasPrice: GasPrice = GAS_PRICE,
91
107
  memo?: string,
108
+ account?: () => Promise<Account>,
92
109
  ): Promise<Uint8Array> {
93
- return this.validatorClient.post.sign(wallet, messaging, zeroFee, gasPrice, memo);
110
+ return this.validatorClient.post.sign(
111
+ wallet,
112
+ messaging,
113
+ zeroFee,
114
+ gasPrice,
115
+ memo,
116
+ account,
117
+ );
94
118
  }
95
119
 
96
120
  /**
@@ -107,8 +131,17 @@ export class CompositeClient {
107
131
  zeroFee: boolean,
108
132
  gasPrice: GasPrice = GAS_PRICE,
109
133
  memo?: string,
134
+ account?: () => Promise<Account>,
110
135
  ): Promise<BroadcastTxAsyncResponse | BroadcastTxSyncResponse | IndexedTx> {
111
- return this.validatorClient.post.send(wallet, messaging, zeroFee, gasPrice, memo);
136
+ return this.validatorClient.post.send(
137
+ wallet,
138
+ messaging,
139
+ zeroFee,
140
+ gasPrice,
141
+ memo,
142
+ undefined,
143
+ account,
144
+ );
112
145
  }
113
146
 
114
147
  /**
@@ -144,8 +177,15 @@ export class CompositeClient {
144
177
  messaging: () => Promise<EncodeObject[]>,
145
178
  gasPrice: GasPrice = GAS_PRICE,
146
179
  memo?: string,
180
+ account?: () => Promise<Account>,
147
181
  ): Promise<StdFee> {
148
- return this.validatorClient.post.simulate(wallet, messaging, gasPrice, memo);
182
+ return this.validatorClient.post.simulate(
183
+ wallet,
184
+ messaging,
185
+ gasPrice,
186
+ memo,
187
+ account,
188
+ );
149
189
  }
150
190
 
151
191
  /**
@@ -155,9 +195,17 @@ export class CompositeClient {
155
195
  * at any point.
156
196
  * @returns The goodTilBlock value
157
197
  */
158
- private async calculateGoodTilBlock(): Promise<number> {
159
- const height = await this.validatorClient.get.latestBlockHeight();
160
- return height + 3;
198
+
199
+ private async calculateGoodTilBlock(
200
+ orderFlags: OrderFlags,
201
+ currentHeight?: number,
202
+ ): Promise<number> {
203
+ if (orderFlags === OrderFlags.SHORT_TERM) {
204
+ const height = currentHeight ?? await this.validatorClient.get.latestBlockHeight();
205
+ return height + SHORT_BLOCK_FORWARD;
206
+ } else {
207
+ return Promise.resolve(0);
208
+ }
161
209
  }
162
210
 
163
211
  /**
@@ -247,10 +295,18 @@ export class CompositeClient {
247
295
  console.log(err);
248
296
  });
249
297
  });
298
+ const account: Promise<Account> = this.validatorClient.post.account(
299
+ subaccount.address,
300
+ undefined,
301
+ );
250
302
  return this.send(
251
303
  subaccount.wallet,
252
304
  () => msgs,
253
- true);
305
+ true,
306
+ undefined,
307
+ undefined,
308
+ () => account,
309
+ );
254
310
  }
255
311
 
256
312
  /**
@@ -272,6 +328,12 @@ export class CompositeClient {
272
328
  * @param execution The execution of the order to place.
273
329
  * @param postOnly The postOnly of the order to place.
274
330
  * @param reduceOnly The reduceOnly of the order to place.
331
+ * @param triggerPrice The trigger price of conditional orders.
332
+ * @param marketInfo optional market information for calculating quantums and subticks.
333
+ * This can be constructed from Indexer API. If set to null, additional round
334
+ * trip to Indexer API will be made.
335
+ * @param currentHeight Current block height. This can be obtained from ValidatorClient.
336
+ * If set to null, additional round trip to ValidatorClient will be made.
275
337
  *
276
338
  *
277
339
  * @throws UnexpectedClientError if a malformed response is returned with no GRPC error
@@ -292,6 +354,8 @@ export class CompositeClient {
292
354
  postOnly?: boolean,
293
355
  reduceOnly?: boolean,
294
356
  triggerPrice?: number,
357
+ marketInfo?: MarketInfo,
358
+ currentHeight?: number,
295
359
  ): Promise<BroadcastTxAsyncResponse | BroadcastTxSyncResponse | IndexedTx> {
296
360
  const msgs: Promise<EncodeObject[]> = new Promise((resolve) => {
297
361
  const msg = this.placeOrderMessage(
@@ -309,15 +373,26 @@ export class CompositeClient {
309
373
  postOnly,
310
374
  reduceOnly,
311
375
  triggerPrice,
376
+ marketInfo,
377
+ currentHeight,
312
378
  );
313
379
  msg.then((it) => resolve([it])).catch((err) => {
314
380
  console.log(err);
315
381
  });
316
382
  });
383
+ const orderFlags = calculateOrderFlags(type, timeInForce);
384
+ const account: Promise<Account> = this.validatorClient.post.account(
385
+ subaccount.address,
386
+ orderFlags,
387
+ );
317
388
  return this.send(
318
389
  subaccount.wallet,
319
390
  () => msgs,
320
- true);
391
+ true,
392
+ undefined,
393
+ undefined,
394
+ () => account,
395
+ );
321
396
  }
322
397
 
323
398
  /**
@@ -360,14 +435,22 @@ export class CompositeClient {
360
435
  postOnly?: boolean,
361
436
  reduceOnly?: boolean,
362
437
  triggerPrice?: number,
438
+ marketInfo?: MarketInfo,
439
+ currentHeight?: number,
363
440
  ): Promise<EncodeObject> {
364
- const marketsResponse = await this.indexerClient.markets.getPerpetualMarkets(marketId);
365
- const market = marketsResponse.markets[marketId];
366
- const clobPairId = market.clobPairId;
367
- const atomicResolution = market.atomicResolution;
368
- const stepBaseQuantums = market.stepBaseQuantums;
369
- const quantumConversionExponent = market.quantumConversionExponent;
370
- const subticksPerTick = market.subticksPerTick;
441
+ const orderFlags = calculateOrderFlags(type, timeInForce);
442
+
443
+ const result = await Promise.all([
444
+ this.calculateGoodTilBlock(orderFlags, currentHeight),
445
+ this.retrieveMarketInfo(marketId, marketInfo),
446
+ ],
447
+ );
448
+ const goodTilBlock = result[0];
449
+ const clobPairId = result[1].clobPairId;
450
+ const atomicResolution = result[1].atomicResolution;
451
+ const stepBaseQuantums = result[1].stepBaseQuantums;
452
+ const quantumConversionExponent = result[1].quantumConversionExponent;
453
+ const subticksPerTick = result[1].subticksPerTick;
371
454
  const orderSide = calculateSide(side);
372
455
  const quantums = calculateQuantums(
373
456
  size,
@@ -380,16 +463,13 @@ export class CompositeClient {
380
463
  quantumConversionExponent,
381
464
  subticksPerTick,
382
465
  );
383
- const orderFlags = calculateOrderFlags(type, timeInForce);
384
466
  const orderTimeInForce = calculateTimeInForce(type, timeInForce, execution, postOnly);
385
- const goodTilBlock = (orderFlags === OrderFlags.SHORT_TERM)
386
- ? await this.calculateGoodTilBlock() : 0;
387
467
  let goodTilBlockTime = 0;
388
468
  if (orderFlags === OrderFlags.LONG_TERM || orderFlags === OrderFlags.CONDITIONAL) {
389
469
  if (goodTilTimeInSeconds == null) {
390
470
  throw new Error('goodTilTimeInSeconds must be set for LONG_TERM or CONDITIONAL order');
391
471
  } else {
392
- goodTilBlockTime = await this.calculateGoodTilBlockTime(goodTilTimeInSeconds);
472
+ goodTilBlockTime = this.calculateGoodTilBlockTime(goodTilTimeInSeconds);
393
473
  }
394
474
  }
395
475
  const clientMetadata = calculateClientMetadata(type);
@@ -419,6 +499,27 @@ export class CompositeClient {
419
499
  );
420
500
  }
421
501
 
502
+ private async retrieveMarketInfo(marketId: string, marketInfo?:MarketInfo): Promise<MarketInfo> {
503
+ if (marketInfo) {
504
+ return Promise.resolve(marketInfo);
505
+ } else {
506
+ const marketsResponse = await this.indexerClient.markets.getPerpetualMarkets(marketId);
507
+ const market = marketsResponse.markets[marketId];
508
+ const clobPairId = market.clobPairId;
509
+ const atomicResolution = market.atomicResolution;
510
+ const stepBaseQuantums = market.stepBaseQuantums;
511
+ const quantumConversionExponent = market.quantumConversionExponent;
512
+ const subticksPerTick = market.subticksPerTick;
513
+ return {
514
+ clobPairId,
515
+ atomicResolution,
516
+ stepBaseQuantums,
517
+ quantumConversionExponent,
518
+ subticksPerTick,
519
+ };
520
+ }
521
+ }
522
+
422
523
  /**
423
524
  * @description Calculate and create the short term place order message
424
525
  *
@@ -787,7 +888,8 @@ export class CompositeClient {
787
888
  const signature = await this.sign(
788
889
  wallet,
789
890
  () => msgs,
790
- true);
891
+ true,
892
+ );
791
893
 
792
894
  return Buffer.from(signature).toString('base64');
793
895
  }
@@ -812,7 +914,11 @@ export class CompositeClient {
812
914
  );
813
915
  resolve([msg]);
814
916
  });
815
- const signature = await this.sign(subaccount.wallet, () => msgs, true);
917
+ const signature = await this.sign(
918
+ subaccount.wallet,
919
+ () => msgs,
920
+ true,
921
+ );
816
922
 
817
923
  return Buffer.from(signature).toString('base64');
818
924
  }
@@ -125,6 +125,8 @@ export const MAX_MEMO_CHARACTERS: number = 256;
125
125
 
126
126
  export const SHORT_BLOCK_WINDOW: number = 20;
127
127
 
128
+ export const SHORT_BLOCK_FORWARD: number = 3;
129
+
128
130
  // Querying
129
131
  export const PAGE_REQUEST: PageRequest = {
130
132
  key: new Uint8Array(),
@@ -426,6 +426,26 @@ export class Get {
426
426
  return BridgeModule.QueryDelayedCompleteBridgeMessagesResponse.decode(data);
427
427
  }
428
428
 
429
+ /**
430
+ * @description Get all validators of a status.
431
+ *
432
+ * @returns all validators of a status.
433
+ */
434
+ async getAllValidators(
435
+ status: string = '',
436
+ ): Promise<StakingModule.QueryValidatorsResponse> {
437
+ const requestData = Uint8Array.from(
438
+ StakingModule.QueryValidatorsRequest
439
+ .encode({ status, pagination: PAGE_REQUEST }).finish(),
440
+ );
441
+
442
+ const data: Uint8Array = await this.sendQuery(
443
+ '/cosmos.staking.v1beta1.Query/Validators',
444
+ requestData,
445
+ );
446
+ return StakingModule.QueryValidatorsResponse.decode(data);
447
+ }
448
+
429
449
  private async sendQuery(requestUrl: string, requestData: Uint8Array): Promise<Uint8Array> {
430
450
  return this.stargateQueryClient.queryUnverified(requestUrl, requestData);
431
451
  }
@@ -5,6 +5,7 @@ import {
5
5
  Registry,
6
6
  } from '@cosmjs/proto-signing';
7
7
  import {
8
+ Account,
8
9
  calculateFee,
9
10
  GasPrice,
10
11
  IndexedTx,
@@ -55,6 +56,8 @@ export class Post {
55
56
  private readonly chainId: string;
56
57
  public readonly get: Get;
57
58
 
59
+ private accountNumberCache: Map<string, Account> = new Map();
60
+
58
61
  constructor(
59
62
  get: Get,
60
63
  chainId: string,
@@ -78,10 +81,20 @@ export class Post {
78
81
  messaging: () => Promise<EncodeObject[]>,
79
82
  gasPrice: GasPrice = GAS_PRICE,
80
83
  memo?: string,
84
+ account?: () => Promise<Account>,
81
85
  ): Promise<StdFee> {
82
- const msgs = await messaging();
83
- const account = await this.get.getAccount(wallet.address!);
84
- return this.simulateTransaction(wallet.pubKey!, account.sequence, msgs, gasPrice, memo);
86
+ const msgsPromise = messaging();
87
+ const accountPromise = account ? (await account()) : this.account(wallet.address!);
88
+ const msgsAndAccount = await Promise.all([msgsPromise, accountPromise]);
89
+ const msgs = msgsAndAccount[0];
90
+
91
+ return this.simulateTransaction(
92
+ wallet.pubKey!,
93
+ msgsAndAccount[1].sequence,
94
+ msgs,
95
+ gasPrice,
96
+ memo,
97
+ );
85
98
  }
86
99
 
87
100
  /**
@@ -98,9 +111,13 @@ export class Post {
98
111
  zeroFee: boolean,
99
112
  gasPrice: GasPrice = GAS_PRICE,
100
113
  memo?: string,
114
+ account?: () => Promise<Account>,
101
115
  ): Promise<Uint8Array> {
102
- const msgs = await messaging();
103
- return this.signTransaction(wallet, msgs, zeroFee, gasPrice, memo);
116
+ const msgsPromise = await messaging();
117
+ const accountPromise = account ? (await account()) : this.account(wallet.address!);
118
+ const msgsAndAccount = await Promise.all([msgsPromise, accountPromise]);
119
+ const msgs = msgsAndAccount[0];
120
+ return this.signTransaction(wallet, msgs, msgsAndAccount[1], zeroFee, gasPrice, memo);
104
121
  }
105
122
 
106
123
  /**
@@ -118,10 +135,16 @@ export class Post {
118
135
  gasPrice: GasPrice = GAS_PRICE,
119
136
  memo?: string,
120
137
  broadcastMode?: BroadcastMode,
138
+ account?: () => Promise<Account>,
121
139
  ): Promise<BroadcastTxAsyncResponse | BroadcastTxSyncResponse | IndexedTx> {
122
- const msgs = await messaging();
140
+ const msgsPromise = messaging();
141
+ const accountPromise = account ? (await account()) : this.account(wallet.address!);
142
+ const msgsAndAccount = await Promise.all([msgsPromise, accountPromise]);
143
+ const msgs = msgsAndAccount[0];
144
+
123
145
  return this.signAndSendTransaction(
124
146
  wallet,
147
+ msgsAndAccount[1],
125
148
  msgs,
126
149
  zeroFee,
127
150
  gasPrice,
@@ -159,11 +182,11 @@ export class Post {
159
182
  private async signTransaction(
160
183
  wallet: LocalWallet,
161
184
  messages: EncodeObject[],
185
+ account: Account,
162
186
  zeroFee: boolean,
163
187
  gasPrice: GasPrice = GAS_PRICE,
164
188
  memo?: string,
165
189
  ): Promise<Uint8Array> {
166
- const account = await this.get.getAccount(wallet.address!);
167
190
  // Simulate transaction if no fee is specified.
168
191
  const fee: StdFee = zeroFee ? {
169
192
  amount: [],
@@ -190,6 +213,23 @@ export class Post {
190
213
  );
191
214
  }
192
215
 
216
+ /**
217
+ * @description Retrieve an account structure for transactions.
218
+ * For short term orders, the sequence doesn't matter. Use cached if available.
219
+ * For long term and conditional orders, a round trip to validator must be made.
220
+ */
221
+ public async account(address: string, orderFlags?: OrderFlags): Promise<Account> {
222
+ if (orderFlags === OrderFlags.SHORT_TERM) {
223
+ if (this.accountNumberCache.has(address)) {
224
+ // For SHORT_TERM orders, the sequence doesn't matter
225
+ return this.accountNumberCache.get(address)!;
226
+ }
227
+ }
228
+ const account = await this.get.getAccount(address);
229
+ this.accountNumberCache.set(address, account);
230
+ return account;
231
+ }
232
+
193
233
  /**
194
234
  * @description Sign and send a message
195
235
  *
@@ -197,6 +237,7 @@ export class Post {
197
237
  */
198
238
  private async signAndSendTransaction(
199
239
  wallet: LocalWallet,
240
+ account: Account,
200
241
  messages: EncodeObject[],
201
242
  zeroFee: boolean,
202
243
  gasPrice: GasPrice = GAS_PRICE,
@@ -206,6 +247,7 @@ export class Post {
206
247
  const signedTransaction = await this.signTransaction(
207
248
  wallet,
208
249
  messages,
250
+ account,
209
251
  zeroFee,
210
252
  gasPrice,
211
253
  memo,
@@ -329,7 +371,16 @@ export class Post {
329
371
  );
330
372
  resolve([msg]);
331
373
  });
332
- return this.send(subaccount.wallet, () => msgs, true, undefined, undefined, broadcastMode);
374
+ const account: Promise<Account> = this.account(subaccount.address, orderFlags);
375
+ return this.send(
376
+ subaccount.wallet,
377
+ () => msgs,
378
+ true,
379
+ undefined,
380
+ undefined,
381
+ broadcastMode,
382
+ () => account,
383
+ );
333
384
  }
334
385
 
335
386
  async placeOrderObject(
@@ -377,7 +428,13 @@ export class Post {
377
428
  );
378
429
  resolve([msg]);
379
430
  });
380
- return this.send(subaccount.wallet, () => msgs, true, undefined, undefined, broadcastMode);
431
+ return this.send(
432
+ subaccount.wallet,
433
+ () => msgs,
434
+ true,
435
+ undefined,
436
+ undefined,
437
+ broadcastMode);
381
438
  }
382
439
 
383
440
  async cancelOrderObject(
@@ -415,7 +472,14 @@ export class Post {
415
472
  );
416
473
  resolve([msg]);
417
474
  });
418
- return this.send(subaccount.wallet, () => msgs, false, undefined, undefined, broadcastMode);
475
+ return this.send(
476
+ subaccount.wallet,
477
+ () => msgs,
478
+ false,
479
+ undefined,
480
+ undefined,
481
+ broadcastMode,
482
+ );
419
483
  }
420
484
 
421
485
  async deposit(
@@ -433,7 +497,14 @@ export class Post {
433
497
  );
434
498
  resolve([msg]);
435
499
  });
436
- return this.send(subaccount.wallet, () => msgs, false, undefined, undefined, broadcastMode);
500
+ return this.send(
501
+ subaccount.wallet,
502
+ () => msgs,
503
+ false,
504
+ undefined,
505
+ undefined,
506
+ broadcastMode,
507
+ );
437
508
  }
438
509
 
439
510
  async withdraw(
@@ -453,7 +524,14 @@ export class Post {
453
524
  );
454
525
  resolve([msg]);
455
526
  });
456
- return this.send(subaccount.wallet, () => msgs, false, undefined, undefined, broadcastMode);
527
+ return this.send(
528
+ subaccount.wallet,
529
+ () => msgs,
530
+ false,
531
+ undefined,
532
+ undefined,
533
+ broadcastMode,
534
+ );
457
535
  }
458
536
 
459
537
  async sendToken(
@@ -13,7 +13,7 @@ import { UserError } from '../lib/errors';
13
13
  import { encodeJson } from '../lib/helpers';
14
14
  import { deriveHDKeyFromEthereumSignature } from '../lib/onboarding';
15
15
  import { NetworkOptimizer } from '../network_optimizer';
16
- import { CompositeClient } from './composite-client';
16
+ import { CompositeClient, MarketInfo } from './composite-client';
17
17
  import {
18
18
  Network, OrderType, OrderSide, OrderTimeInForce, OrderExecution, IndexerConfig, ValidatorConfig,
19
19
  } from './constants';
@@ -224,6 +224,9 @@ export async function placeOrder(
224
224
  const reduceOnly = json.reduceOnly ?? false;
225
225
  const triggerPrice = json.triggerPrice;
226
226
 
227
+ const marketInfo = json.marketInfo as MarketInfo;
228
+ const currentHeight = json.currentHeight as number;
229
+
227
230
  const subaccount = new Subaccount(wallet, subaccountNumber);
228
231
  const tx = await client.placeOrder(
229
232
  subaccount,
@@ -239,6 +242,8 @@ export async function placeOrder(
239
242
  postOnly,
240
243
  reduceOnly,
241
244
  triggerPrice,
245
+ marketInfo,
246
+ currentHeight,
242
247
  );
243
248
  return encodeJson(tx);
244
249
  } catch (error) {
@@ -580,9 +585,13 @@ export async function simulateDeposit(
580
585
  );
581
586
  const msgs: EncodeObject[] = [msg];
582
587
  const encodeObjects: Promise<EncodeObject[]> = new Promise((resolve) => resolve(msgs));
583
- const stdFee = await client.simulate(globalThis.wallet, () => {
584
- return encodeObjects;
585
- });
588
+
589
+ const stdFee = await client.simulate(
590
+ globalThis.wallet,
591
+ () => {
592
+ return encodeObjects;
593
+ },
594
+ );
586
595
  return JSON.stringify(stdFee);
587
596
  } catch (error) {
588
597
  return wrappedError(error);
@@ -619,9 +628,13 @@ export async function simulateWithdraw(
619
628
  );
620
629
  const msgs: EncodeObject[] = [msg];
621
630
  const encodeObjects: Promise<EncodeObject[]> = new Promise((resolve) => resolve(msgs));
622
- const stdFee = await client.simulate(globalThis.wallet, () => {
623
- return encodeObjects;
624
- });
631
+
632
+ const stdFee = await client.simulate(
633
+ globalThis.wallet,
634
+ () => {
635
+ return encodeObjects;
636
+ },
637
+ );
625
638
  return encodeJson(stdFee);
626
639
  } catch (error) {
627
640
  return wrappedError(error);
@@ -662,6 +675,7 @@ export async function simulateTransferNativeToken(
662
675
  );
663
676
  const msgs: EncodeObject[] = [msg];
664
677
  const encodeObjects: Promise<EncodeObject[]> = new Promise((resolve) => resolve(msgs));
678
+
665
679
  const stdFee = await client.simulate(
666
680
  globalThis.wallet,
667
681
  () => {