@dydxprotocol/v4-client-js 2.4.0 → 2.5.1
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 +2 -8
- package/build/cjs/examples/constants.js +4 -2
- package/build/cjs/examples/permissioned_keys_example.js +52 -29
- package/build/cjs/src/clients/composite-client.js +30 -20
- package/build/cjs/src/clients/constants.js +3 -2
- package/build/cjs/src/clients/indexer-client.js +5 -5
- package/build/cjs/src/clients/modules/rest.js +11 -5
- package/build/cjs/src/clients/socket-client.js +6 -2
- package/build/cjs/src/lib/utils.js +7 -1
- 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 +53 -30
- package/build/esm/src/clients/composite-client.d.ts.map +1 -1
- package/build/esm/src/clients/composite-client.js +30 -20
- package/build/esm/src/clients/constants.d.ts +3 -1
- package/build/esm/src/clients/constants.d.ts.map +1 -1
- package/build/esm/src/clients/constants.js +3 -2
- package/build/esm/src/clients/indexer-client.js +5 -5
- package/build/esm/src/clients/modules/rest.d.ts +3 -1
- package/build/esm/src/clients/modules/rest.d.ts.map +1 -1
- package/build/esm/src/clients/modules/rest.js +8 -5
- package/build/esm/src/clients/socket-client.d.ts +1 -0
- package/build/esm/src/clients/socket-client.d.ts.map +1 -1
- package/build/esm/src/clients/socket-client.js +6 -2
- package/build/esm/src/lib/utils.d.ts +3 -0
- package/build/esm/src/lib/utils.d.ts.map +1 -1
- package/build/esm/src/lib/utils.js +6 -1
- package/build/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/examples/constants.ts +3 -0
- package/examples/permissioned_keys_example.ts +58 -29
- package/package.json +2 -1
- package/src/clients/composite-client.ts +28 -21
- package/src/clients/constants.ts +4 -1
- package/src/clients/indexer-client.ts +4 -4
- package/src/clients/modules/rest.ts +10 -4
- package/src/clients/socket-client.ts +7 -1
- package/src/lib/utils.ts +8 -0
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
|
|
|
@@ -8,11 +8,12 @@ import { CompositeClient } from '../src/clients/composite-client';
|
|
|
8
8
|
import { AuthenticatorType, Network, OrderSide, SelectedGasDenom } from '../src/clients/constants';
|
|
9
9
|
import LocalWallet from '../src/clients/modules/local-wallet';
|
|
10
10
|
import { SubaccountInfo } from '../src/clients/subaccount';
|
|
11
|
-
import { DYDX_TEST_MNEMONIC, DYDX_TEST_MNEMONIC_2 } from './constants';
|
|
11
|
+
import { DYDX_TEST_MNEMONIC, DYDX_TEST_MNEMONIC_2, DYDX_TEST_MNEMONIC_3 } from './constants';
|
|
12
12
|
|
|
13
13
|
async function test(): Promise<void> {
|
|
14
14
|
const wallet1 = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC, BECH32_PREFIX);
|
|
15
15
|
const wallet2 = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC_2, BECH32_PREFIX);
|
|
16
|
+
const wallet3 = await LocalWallet.fromMnemonic(DYDX_TEST_MNEMONIC_3, BECH32_PREFIX);
|
|
16
17
|
|
|
17
18
|
const network = Network.staging();
|
|
18
19
|
const client = await CompositeClient.connect(network);
|
|
@@ -23,28 +24,53 @@ async function test(): Promise<void> {
|
|
|
23
24
|
|
|
24
25
|
const subaccount1 = new SubaccountInfo(wallet1, 0);
|
|
25
26
|
const subaccount2 = new SubaccountInfo(wallet2, 0);
|
|
27
|
+
const subaccount3 = new SubaccountInfo(wallet3, 0);
|
|
26
28
|
|
|
27
29
|
// Change second wallet pubkey
|
|
28
30
|
// Add an authenticator to allow wallet2 to place orders
|
|
29
31
|
console.log("** Adding authenticator **");
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
32
|
+
// Record authenticator count before adding
|
|
33
|
+
const authsBefore = await client.getAuthenticators(wallet1.address!);
|
|
34
|
+
const beforeCount = authsBefore.accountAuthenticators.length;
|
|
35
|
+
console.log(`Authenticators before: ${beforeCount}`);
|
|
36
|
+
await addTestAuthenticator(client, subaccount1, wallet2.pubKey!.value);
|
|
37
|
+
|
|
38
|
+
console.log("** Waiting 3 seconds for txn to be confirmed **");
|
|
39
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
40
|
+
|
|
41
|
+
const authsAfter = await client.getAuthenticators(wallet1.address!);
|
|
42
|
+
const afterCount = authsAfter.accountAuthenticators.length;
|
|
43
|
+
console.log(`Authenticators after: ${afterCount}`);
|
|
44
|
+
if (afterCount !== beforeCount + 1) {
|
|
45
|
+
console.error('Authenticator count did not increment by 1.');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
} else {
|
|
48
|
+
console.log('Authenticator count incremented by 1 as expected.');
|
|
49
|
+
}
|
|
33
50
|
// Last element in authenticators array is the most recently created
|
|
34
|
-
const lastElement =
|
|
35
|
-
const
|
|
51
|
+
const lastElement = authsAfter.accountAuthenticators.length - 1;
|
|
52
|
+
const newAuthenticatorID = authsAfter.accountAuthenticators[lastElement].id;
|
|
53
|
+
|
|
54
|
+
console.log(`New authenticator ID: ${newAuthenticatorID}`);
|
|
36
55
|
|
|
37
56
|
// Placing order using subaccount2 for subaccount1 succeeds
|
|
38
|
-
console.log("** Placing order with authenticator **");
|
|
39
|
-
await placeOrder(client, subaccount2, subaccount1,
|
|
57
|
+
console.log("** Placing order for subaccount1 with subaccount2 + authenticator, should succeed **");
|
|
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
65
|
console.log("** Removing authenticator **");
|
|
43
|
-
await removeAuthenticator(client, subaccount1,
|
|
66
|
+
await removeAuthenticator(client, subaccount1, newAuthenticatorID);
|
|
44
67
|
|
|
68
|
+
console.log("** Waiting 3 seconds for txn to be confirmed **");
|
|
69
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
70
|
+
|
|
45
71
|
// Placing an order using subaccount2 will now fail
|
|
46
|
-
console.log("** Placing order with
|
|
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.
|
|
3
|
+
"version": "2.5.1",
|
|
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",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"cosmjs-types": "^0.9.0",
|
|
63
63
|
"ethereum-cryptography": "^2.0.0",
|
|
64
64
|
"ethers": "^6.6.1",
|
|
65
|
+
"https-proxy-agent": "^7.0.6",
|
|
65
66
|
"long": "^4.0.0",
|
|
66
67
|
"protobufjs": ">=6.11.4",
|
|
67
68
|
"ws": "^8.14.2"
|
|
@@ -1084,7 +1084,7 @@ export class CompositeClient {
|
|
|
1084
1084
|
console.log(err);
|
|
1085
1085
|
});
|
|
1086
1086
|
});
|
|
1087
|
-
const signature = await this.sign(wallet, () => msgs, true);
|
|
1087
|
+
const signature = await this.sign(subaccount.wallet, () => msgs, true);
|
|
1088
1088
|
|
|
1089
1089
|
return Buffer.from(signature).toString('base64');
|
|
1090
1090
|
}
|
|
@@ -1414,34 +1414,41 @@ export class CompositeClient {
|
|
|
1414
1414
|
}
|
|
1415
1415
|
|
|
1416
1416
|
validateAuthenticator(authenticator: Authenticator): boolean {
|
|
1417
|
-
|
|
1418
|
-
if (
|
|
1419
|
-
return
|
|
1417
|
+
const decodeCompositeConfig = (config: unknown): Authenticator[] | null => {
|
|
1418
|
+
if (Array.isArray(config)) {
|
|
1419
|
+
return config as Authenticator[];
|
|
1420
1420
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1421
|
+
if (typeof config === 'string') {
|
|
1422
|
+
try {
|
|
1423
|
+
const decoded = Buffer.from(config, 'base64').toString('utf8');
|
|
1424
|
+
const parsed = JSON.parse(decoded);
|
|
1425
|
+
return Array.isArray(parsed) ? (parsed as Authenticator[]) : null;
|
|
1426
|
+
} catch {
|
|
1427
|
+
return null;
|
|
1428
|
+
}
|
|
1424
1429
|
}
|
|
1430
|
+
return null;
|
|
1431
|
+
};
|
|
1425
1432
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
return
|
|
1433
|
+
const checkAuthenticator = (auth: Authenticator): boolean => {
|
|
1434
|
+
if (auth.type === AuthenticatorType.SIGNATURE_VERIFICATION) {
|
|
1435
|
+
return true;
|
|
1429
1436
|
}
|
|
1430
1437
|
|
|
1431
|
-
if (auth.type === AuthenticatorType.ALL_OF) {
|
|
1432
|
-
|
|
1433
|
-
|
|
1438
|
+
if (auth.type === AuthenticatorType.ANY_OF || auth.type === AuthenticatorType.ALL_OF) {
|
|
1439
|
+
const subAuthenticators = decodeCompositeConfig(auth.config);
|
|
1440
|
+
if (subAuthenticators == null) {
|
|
1441
|
+
return false;
|
|
1442
|
+
}
|
|
1443
|
+
if (auth.type === AuthenticatorType.ANY_OF) {
|
|
1444
|
+
return subAuthenticators.every((nested) => checkAuthenticator(nested));
|
|
1445
|
+
}
|
|
1446
|
+
return subAuthenticators.some((nested) => checkAuthenticator(nested));
|
|
1434
1447
|
}
|
|
1435
1448
|
|
|
1436
|
-
// If it's a base-case authenticator but not SignatureVerification, it's unsafe
|
|
1437
|
-
return false;
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
// The top-level authenticator must pass validation
|
|
1441
|
-
if (!checkAuthenticator(authenticator)) {
|
|
1442
1449
|
return false;
|
|
1443
|
-
}
|
|
1450
|
+
};
|
|
1444
1451
|
|
|
1445
|
-
return
|
|
1452
|
+
return checkAuthenticator(authenticator);
|
|
1446
1453
|
}
|
|
1447
1454
|
}
|
package/src/clients/constants.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PageRequest } from '@dydxprotocol/v4-proto/src/codegen/cosmos/base/query/v1beta1/pagination';
|
|
2
|
+
import { AxiosProxyConfig } from 'axios';
|
|
2
3
|
import Long from 'long';
|
|
3
4
|
|
|
4
5
|
import { BroadcastOptions, DenomConfig } from './types';
|
|
@@ -243,10 +244,12 @@ export const PAGE_REQUEST: PageRequest = {
|
|
|
243
244
|
export class IndexerConfig {
|
|
244
245
|
public restEndpoint: string;
|
|
245
246
|
public websocketEndpoint: string;
|
|
247
|
+
public proxy?: AxiosProxyConfig;
|
|
246
248
|
|
|
247
|
-
constructor(restEndpoint: string, websocketEndpoint: string) {
|
|
249
|
+
constructor(restEndpoint: string, websocketEndpoint: string, proxy?: AxiosProxyConfig) {
|
|
248
250
|
this.restEndpoint = restEndpoint;
|
|
249
251
|
this.websocketEndpoint = websocketEndpoint;
|
|
252
|
+
this.proxy = proxy;
|
|
250
253
|
}
|
|
251
254
|
}
|
|
252
255
|
|
|
@@ -19,10 +19,10 @@ export class IndexerClient {
|
|
|
19
19
|
this.config = config;
|
|
20
20
|
this.apiTimeout = apiTimeout ?? DEFAULT_API_TIMEOUT;
|
|
21
21
|
|
|
22
|
-
this._markets = new MarketsClient(config.restEndpoint);
|
|
23
|
-
this._account = new AccountClient(config.restEndpoint);
|
|
24
|
-
this._utility = new UtilityClient(config.restEndpoint);
|
|
25
|
-
this._vault = new VaultClient(config.restEndpoint);
|
|
22
|
+
this._markets = new MarketsClient(config.restEndpoint, apiTimeout, config.proxy);
|
|
23
|
+
this._account = new AccountClient(config.restEndpoint, apiTimeout, config.proxy);
|
|
24
|
+
this._utility = new UtilityClient(config.restEndpoint, apiTimeout, config.proxy);
|
|
25
|
+
this._vault = new VaultClient(config.restEndpoint, apiTimeout, config.proxy);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
@@ -1,24 +1,30 @@
|
|
|
1
|
+
import axios, { AxiosInstance, AxiosProxyConfig } from 'axios';
|
|
2
|
+
|
|
1
3
|
import { DEFAULT_API_TIMEOUT } from '../constants';
|
|
2
4
|
import { generateQueryPath } from '../helpers/request-helpers';
|
|
3
|
-
import {
|
|
5
|
+
import { Response } from '../lib/axios';
|
|
4
6
|
import { Data } from '../types';
|
|
5
7
|
|
|
6
8
|
export default class RestClient {
|
|
7
9
|
readonly host: string;
|
|
8
10
|
readonly apiTimeout: Number;
|
|
11
|
+
readonly axiosInstance: AxiosInstance;
|
|
9
12
|
|
|
10
|
-
constructor(host: string, apiTimeout?: Number | null) {
|
|
13
|
+
constructor(host: string, apiTimeout?: Number | null, proxy?: AxiosProxyConfig) {
|
|
11
14
|
if (host.endsWith('/')) {
|
|
12
15
|
this.host = host.slice(0, -1);
|
|
13
16
|
} else {
|
|
14
17
|
this.host = host;
|
|
15
18
|
}
|
|
16
19
|
this.apiTimeout = apiTimeout || DEFAULT_API_TIMEOUT;
|
|
20
|
+
this.axiosInstance = axios.create({
|
|
21
|
+
proxy,
|
|
22
|
+
});
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
async get(requestPath: string, params: {} = {}): Promise<Data> {
|
|
20
26
|
const url = `${this.host}${generateQueryPath(requestPath, params)}`;
|
|
21
|
-
const response = await
|
|
27
|
+
const response = await this.axiosInstance.get(url);
|
|
22
28
|
return response.data;
|
|
23
29
|
}
|
|
24
30
|
|
|
@@ -29,6 +35,6 @@ export default class RestClient {
|
|
|
29
35
|
headers: {} = {},
|
|
30
36
|
): Promise<Response> {
|
|
31
37
|
const url = `${this.host}${generateQueryPath(requestPath, params)}`;
|
|
32
|
-
return
|
|
38
|
+
return this.axiosInstance.post(url, body, { headers });
|
|
33
39
|
}
|
|
34
40
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
1
2
|
import WebSocket, { ErrorEvent, MessageEvent } from 'ws';
|
|
2
3
|
|
|
4
|
+
import { getProxyAgent } from '../lib/utils';
|
|
3
5
|
import { IndexerConfig } from './constants';
|
|
4
6
|
|
|
5
7
|
enum OutgoingMessageTypes {
|
|
@@ -37,6 +39,7 @@ export enum CandlesResolution {
|
|
|
37
39
|
export class SocketClient {
|
|
38
40
|
private url: string;
|
|
39
41
|
private ws?: WebSocket;
|
|
42
|
+
private proxyAgent?: HttpsProxyAgent<string>;
|
|
40
43
|
private onOpenCallback?: () => void;
|
|
41
44
|
private onCloseCallback?: () => void;
|
|
42
45
|
private onMessageCallback?: (event: MessageEvent) => void;
|
|
@@ -51,6 +54,7 @@ export class SocketClient {
|
|
|
51
54
|
onErrorCallback: (event: ErrorEvent) => void,
|
|
52
55
|
) {
|
|
53
56
|
this.url = config.websocketEndpoint;
|
|
57
|
+
this.proxyAgent = config.proxy ? getProxyAgent(config.proxy) : undefined;
|
|
54
58
|
this.onOpenCallback = onOpenCallback;
|
|
55
59
|
this.onCloseCallback = onCloseCallback;
|
|
56
60
|
this.onMessageCallback = onMessageCallback;
|
|
@@ -58,7 +62,9 @@ export class SocketClient {
|
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
connect(): void {
|
|
61
|
-
this.ws = new WebSocket(this.url
|
|
65
|
+
this.ws = new WebSocket(this.url, {
|
|
66
|
+
agent: this.proxyAgent,
|
|
67
|
+
});
|
|
62
68
|
this.ws.addEventListener('open', this.handleOpen.bind(this));
|
|
63
69
|
this.ws.addEventListener('close', this.handleClose.bind(this));
|
|
64
70
|
this.ws.addEventListener('message', this.handleMessage.bind(this));
|
package/src/lib/utils.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { AxiosProxyConfig } from 'axios';
|
|
2
|
+
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
3
|
+
|
|
1
4
|
import { MAX_UINT_32 } from '../clients/constants';
|
|
2
5
|
|
|
3
6
|
/**
|
|
@@ -74,3 +77,8 @@ export function calculateClockOffsetFromFetchDateHeader(
|
|
|
74
77
|
|
|
75
78
|
return Math.floor(offset);
|
|
76
79
|
}
|
|
80
|
+
|
|
81
|
+
export function getProxyAgent(proxy: AxiosProxyConfig): HttpsProxyAgent<string> {
|
|
82
|
+
const auth = proxy.auth ? `${proxy.auth.username}:${proxy.auth.password}@` : '';
|
|
83
|
+
return new HttpsProxyAgent(`${proxy.protocol}://${auth}${proxy.host}:${proxy.port}`);
|
|
84
|
+
}
|