@dydxprotocol/v4-client-js 2.5.0 → 2.5.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.
@@ -17,6 +17,9 @@ export const DYDX_LOCAL_MNEMONIC =
17
17
 
18
18
  export const DYDX_TEST_MNEMONIC_2 = 'movie yard still copper exile wear brisk chest ride dizzy novel future menu finish radar lunar claim hub middle force turtle mouse frequent embark';
19
19
 
20
+ // dydx1fxu2ndxwju2evh28js0cflkxn5wxzvlhw4gec5
21
+ export const DYDX_TEST_MNEMONIC_3 = 'crane poverty boil edit response weasel quick almost fringe able upon benefit boil boat narrow innocent enter cradle tongue toward luggage awful mention column';
22
+
20
23
  export const MARKET_BTC_USD: string = 'BTC-USD';
21
24
  export const PERPETUAL_PAIR_BTC_USD: number = 0;
22
25
 
@@ -1,5 +1,3 @@
1
- import { TextEncoder } from 'util';
2
-
3
1
  import { toBase64 } from '@cosmjs/encoding';
4
2
  import { Order_TimeInForce } from '@dydxprotocol/v4-proto/src/codegen/dydxprotocol/clob/order';
5
3
 
@@ -8,11 +6,12 @@ import { CompositeClient } from '../src/clients/composite-client';
8
6
  import { AuthenticatorType, Network, OrderSide, SelectedGasDenom } from '../src/clients/constants';
9
7
  import LocalWallet from '../src/clients/modules/local-wallet';
10
8
  import { SubaccountInfo } from '../src/clients/subaccount';
11
- import { DYDX_TEST_MNEMONIC, DYDX_TEST_MNEMONIC_2 } from './constants';
9
+ import { DYDX_TEST_MNEMONIC, DYDX_TEST_MNEMONIC_2, DYDX_TEST_MNEMONIC_3 } from './constants';
12
10
 
13
11
  async function test(): Promise<void> {
14
12
  const wallet1 = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC, BECH32_PREFIX);
15
13
  const wallet2 = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC_2, BECH32_PREFIX);
14
+ const wallet3 = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC_3, BECH32_PREFIX);
16
15
 
17
16
  const network = Network.staging();
18
17
  const client = await CompositeClient.connect(network);
@@ -23,28 +22,55 @@ async function test(): Promise<void> {
23
22
 
24
23
  const subaccount1 = new SubaccountInfo(wallet1, 0);
25
24
  const subaccount2 = new SubaccountInfo(wallet2, 0);
25
+ const subaccount3 = new SubaccountInfo(wallet3, 0);
26
26
 
27
27
  // Change second wallet pubkey
28
28
  // Add an authenticator to allow wallet2 to place orders
29
- console.log("** Adding authenticator **");
30
- await addAuthenticator(client, subaccount1, wallet2.pubKey!.value);
31
-
32
- const authenticators = await client.getAuthenticators(wallet1.address!);
29
+ console.log('** Adding authenticator **');
30
+ // Record authenticator count before adding
31
+ const authsBefore = await client.getAuthenticators(wallet1.address!);
32
+ const beforeCount = authsBefore.accountAuthenticators.length;
33
+ console.log(`Authenticators before: ${beforeCount}`);
34
+ await addTestAuthenticator(client, subaccount1, wallet2.pubKey!.value);
35
+
36
+ console.log('** Waiting 3 seconds for txn to be confirmed **');
37
+ await new Promise((resolve) => setTimeout(resolve, 3000));
38
+
39
+ const authsAfter = await client.getAuthenticators(wallet1.address!);
40
+ const afterCount = authsAfter.accountAuthenticators.length;
41
+ console.log(`Authenticators after: ${afterCount}`);
42
+ if (afterCount !== beforeCount + 1) {
43
+ console.error('Authenticator count did not increment by 1.');
44
+ process.exit(1);
45
+ } else {
46
+ console.log('Authenticator count incremented by 1 as expected.');
47
+ }
33
48
  // Last element in authenticators array is the most recently created
34
- const lastElement = authenticators.accountAuthenticators.length - 1;
35
- const authenticatorID = authenticators.accountAuthenticators[lastElement].id;
49
+ const lastElement = authsAfter.accountAuthenticators.length - 1;
50
+ const newAuthenticatorID = authsAfter.accountAuthenticators[lastElement].id;
51
+
52
+ console.log(`New authenticator ID: ${newAuthenticatorID}`);
36
53
 
37
54
  // Placing order using subaccount2 for subaccount1 succeeds
38
- console.log("** Placing order with authenticator **");
39
- await placeOrder(client, subaccount2, subaccount1, authenticatorID);
55
+ console.log(
56
+ '** Placing order for subaccount1 with subaccount2 + authenticator, should succeed **',
57
+ );
58
+ await placeOrder(client, subaccount2, subaccount1, newAuthenticatorID);
59
+
60
+ // Placing order using subaccount3 for subaccount1 should fail
61
+ console.log('** Placing order for subaccount1 with subaccount3 + authenticator, should fail **');
62
+ await placeOrder(client, subaccount3, subaccount1, newAuthenticatorID);
40
63
 
41
64
  // Remove authenticator
42
- console.log("** Removing authenticator **");
43
- await removeAuthenticator(client, subaccount1, authenticatorID);
65
+ console.log('** Removing authenticator **');
66
+ await removeAuthenticator(client, subaccount1, newAuthenticatorID);
67
+
68
+ console.log('** Waiting 3 seconds for txn to be confirmed **');
69
+ await new Promise((resolve) => setTimeout(resolve, 3000));
44
70
 
45
71
  // Placing an order using subaccount2 will now fail
46
- console.log("** Placing order with invalid authenticator should fail **");
47
- await placeOrder(client, subaccount2, subaccount1, authenticatorID);
72
+ console.log('** Placing order with removed authenticator should fail **');
73
+ await placeOrder(client, subaccount2, subaccount1, newAuthenticatorID);
48
74
  }
49
75
 
50
76
  async function removeAuthenticator(
@@ -55,38 +81,41 @@ async function removeAuthenticator(
55
81
  await client.removeAuthenticator(subaccount, id);
56
82
  }
57
83
 
58
- async function addAuthenticator(
84
+ async function addTestAuthenticator(
59
85
  client: CompositeClient,
60
86
  subaccount: SubaccountInfo,
61
87
  authedPubKey: string,
62
88
  ): Promise<void> {
63
- const subAuth = [ {
64
- type: AuthenticatorType.SIGNATURE_VERIFICATION,
65
- config: authedPubKey,
66
- },
67
- {
68
- type: AuthenticatorType.ANY_OF,
69
- config: [
89
+ const msgType = (s: string): string => toBase64(new TextEncoder().encode(s));
90
+ const anyOfSubAuth = [
70
91
  {
71
92
  type: AuthenticatorType.MESSAGE_FILTER,
72
- config: toBase64(new TextEncoder().encode('/dydxprotocol.clob.MsgPlaceOrder')),
93
+ config: msgType('/dydxprotocol.clob.MsgPlaceOrder'),
73
94
  },
74
95
  {
75
96
  type: AuthenticatorType.MESSAGE_FILTER,
76
- config: toBase64(new TextEncoder().encode('/dydxprotocol.clob.MsgPlaceOrder')),
97
+ config: msgType('/dydxprotocol.sending.MsgCreateTransfer'),
77
98
  },
78
- ]
79
- }
80
- ];
99
+ ];
100
+
101
+ // Nested AnyOf config must be base64(JSON([...])) as on-chain expects []byte
102
+ const anyOfConfigB64 = toBase64(new TextEncoder().encode(JSON.stringify(anyOfSubAuth)));
103
+
104
+ const subAuth = [
105
+ {
106
+ type: AuthenticatorType.SIGNATURE_VERIFICATION,
107
+ config: authedPubKey,
108
+ },
109
+ {
110
+ type: AuthenticatorType.ANY_OF,
111
+ config: anyOfConfigB64,
112
+ },
113
+ ];
81
114
 
82
115
  const jsonString = JSON.stringify(subAuth);
83
116
  const encodedData = new TextEncoder().encode(jsonString);
84
117
 
85
- try {
86
- await client.addAuthenticator(subaccount, AuthenticatorType.ALL_OF, encodedData);
87
- } catch (error) {
88
- console.log(error.message);
89
- }
118
+ await client.addAuthenticator(subaccount, AuthenticatorType.ALL_OF, encodedData);
90
119
  }
91
120
 
92
121
  async function placeOrder(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dydxprotocol/v4-client-js",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "General client library for the new dYdX system (v4 decentralized)",
5
5
  "main": "build/cjs/src/index.js",
6
6
  "module": "build/esm/src/index.js",
@@ -77,6 +77,7 @@
77
77
  "@semantic-release/changelog": "^6.0.3",
78
78
  "@semantic-release/npm": "^12.0.1",
79
79
  "@types/ffi-napi": "^4.0.4",
80
+ "@types/node": "^20.3.1",
80
81
  "@types/ws": "8.5.4",
81
82
  "@typescript-eslint/eslint-plugin": "v5.42.0",
82
83
  "@typescript-eslint/parser": "v5.42.0",
@@ -1,5 +1,3 @@
1
- import { TextDecoder } from 'util';
2
-
3
1
  import { EncodeObject } from '@cosmjs/proto-signing';
4
2
  import { Account, GasPrice, IndexedTx, StdFee } from '@cosmjs/stargate';
5
3
  import { Method } from '@cosmjs/tendermint-rpc';
@@ -1084,7 +1082,7 @@ export class CompositeClient {
1084
1082
  console.log(err);
1085
1083
  });
1086
1084
  });
1087
- const signature = await this.sign(wallet, () => msgs, true);
1085
+ const signature = await this.sign(subaccount.wallet, () => msgs, true);
1088
1086
 
1089
1087
  return Buffer.from(signature).toString('base64');
1090
1088
  }
@@ -1414,34 +1412,41 @@ export class CompositeClient {
1414
1412
  }
1415
1413
 
1416
1414
  validateAuthenticator(authenticator: Authenticator): boolean {
1417
- function checkAuthenticator(auth: Authenticator): boolean {
1418
- if (auth.type === AuthenticatorType.SIGNATURE_VERIFICATION) {
1419
- return true; // A SignatureVerification authenticator is safe.
1415
+ const decodeCompositeConfig = (config: unknown): Authenticator[] | null => {
1416
+ if (Array.isArray(config)) {
1417
+ return config as Authenticator[];
1420
1418
  }
1421
-
1422
- if (!Array.isArray(auth.config)) {
1423
- return false; // Unsafe case: a non-array config for a composite authenticator
1419
+ if (typeof config === 'string') {
1420
+ try {
1421
+ const decoded = Buffer.from(config, 'base64').toString('utf8');
1422
+ const parsed = JSON.parse(decoded);
1423
+ return Array.isArray(parsed) ? (parsed as Authenticator[]) : null;
1424
+ } catch {
1425
+ return null;
1426
+ }
1424
1427
  }
1428
+ return null;
1429
+ };
1425
1430
 
1426
- if (auth.type === AuthenticatorType.ANY_OF) {
1427
- // ANY_OF is safe only if ALL sub-authenticators return true
1428
- return auth.config.every((nestedAuth) => checkAuthenticator(nestedAuth));
1431
+ const checkAuthenticator = (auth: Authenticator): boolean => {
1432
+ if (auth.type === AuthenticatorType.SIGNATURE_VERIFICATION) {
1433
+ return true;
1429
1434
  }
1430
1435
 
1431
- if (auth.type === AuthenticatorType.ALL_OF) {
1432
- // ALL_OF is safe if at least one sub-authenticator returns true
1433
- return auth.config.some((nestedAuth) => checkAuthenticator(nestedAuth));
1436
+ if (auth.type === AuthenticatorType.ANY_OF || auth.type === AuthenticatorType.ALL_OF) {
1437
+ const subAuthenticators = decodeCompositeConfig(auth.config);
1438
+ if (subAuthenticators == null) {
1439
+ return false;
1440
+ }
1441
+ if (auth.type === AuthenticatorType.ANY_OF) {
1442
+ return subAuthenticators.every((nested) => checkAuthenticator(nested));
1443
+ }
1444
+ return subAuthenticators.some((nested) => checkAuthenticator(nested));
1434
1445
  }
1435
1446
 
1436
- // If it's a base-case authenticator but not SignatureVerification, it's unsafe
1437
1447
  return false;
1438
- }
1439
-
1440
- // The top-level authenticator must pass validation
1441
- if (!checkAuthenticator(authenticator)) {
1442
- return false;
1443
- }
1448
+ };
1444
1449
 
1445
- return true;
1450
+ return checkAuthenticator(authenticator);
1446
1451
  }
1447
1452
  }