@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.
- package/CHANGELOG.md +3 -3
- package/build/cjs/examples/constants.js +4 -2
- package/build/cjs/examples/permissioned_keys_example.js +55 -33
- package/build/cjs/src/clients/composite-client.js +31 -22
- package/build/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/build/esm/examples/constants.d.ts +1 -0
- package/build/esm/examples/constants.d.ts.map +1 -1
- package/build/esm/examples/constants.js +3 -1
- package/build/esm/examples/permissioned_keys_example.js +55 -33
- package/build/esm/src/clients/composite-client.d.ts.map +1 -1
- package/build/esm/src/clients/composite-client.js +30 -21
- package/build/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/examples/constants.ts +3 -0
- package/examples/permissioned_keys_example.ts +62 -33
- package/package.json +2 -1
- package/src/clients/composite-client.ts +28 -23
package/examples/constants.ts
CHANGED
|
@@ -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(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
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 =
|
|
35
|
-
const
|
|
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(
|
|
39
|
-
|
|
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(
|
|
43
|
-
await removeAuthenticator(client, subaccount1,
|
|
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(
|
|
47
|
-
await placeOrder(client, subaccount2, subaccount1,
|
|
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
|
|
84
|
+
async function addTestAuthenticator(
|
|
59
85
|
client: CompositeClient,
|
|
60
86
|
subaccount: SubaccountInfo,
|
|
61
87
|
authedPubKey: string,
|
|
62
88
|
): Promise<void> {
|
|
63
|
-
const
|
|
64
|
-
|
|
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:
|
|
93
|
+
config: msgType('/dydxprotocol.clob.MsgPlaceOrder'),
|
|
73
94
|
},
|
|
74
95
|
{
|
|
75
96
|
type: AuthenticatorType.MESSAGE_FILTER,
|
|
76
|
-
config:
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
1418
|
-
if (
|
|
1419
|
-
return
|
|
1415
|
+
const decodeCompositeConfig = (config: unknown): Authenticator[] | null => {
|
|
1416
|
+
if (Array.isArray(config)) {
|
|
1417
|
+
return config as Authenticator[];
|
|
1420
1418
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
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
|
-
|
|
1427
|
-
|
|
1428
|
-
return
|
|
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
|
-
|
|
1433
|
-
|
|
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
|
|
1450
|
+
return checkAuthenticator(authenticator);
|
|
1446
1451
|
}
|
|
1447
1452
|
}
|