asterdex 1.0.0

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 (3) hide show
  1. package/README.md +77 -0
  2. package/index.js +157 -0
  3. package/package.json +19 -0
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # About
2
+
3
+ A simple AsterDex.com api for REST api: https://github.com/asterdex/api-docs.
4
+
5
+ ## Usage example 1 (api class)
6
+
7
+ ```
8
+ const api = require('asterdex')
9
+ let ac = new api("<APIKEY>", "<SECRET>");
10
+ async function f(){
11
+ let r = await ac.get('/fapi/v2/balance')
12
+ console.log(r)
13
+ }
14
+ f()
15
+ ```
16
+
17
+ ## Usage example 2 (call function)
18
+
19
+ ```
20
+ const {call} = require('asterdex')
21
+ let apiKey = "<APIKEY>";
22
+ let secret = "<SECRET>";
23
+ let path = "/fapi/v2/balance";
24
+ async function f() {
25
+ let r = await call(path, { apiKey, secret, data, method, raw });
26
+ console.log(r);
27
+ }
28
+ f();
29
+ ```
30
+
31
+ ### Parameters 1 (api class)
32
+
33
+ ```
34
+ let ac = new api("<APIKEY>", "<SECRET>", {testnet});
35
+ ac.get(path, data, { raw }) // GET
36
+ ac.post(path, data, { raw }) // POST
37
+ ac.put(path, data, { raw }) // PUT
38
+ ac.delete(path, data, { raw }) // DELETE
39
+ ac.getPublic(path, data, { raw }) // GET public endpoint // alt: ac.call(path[, data])
40
+ ac.call() // refer above
41
+ ```
42
+
43
+ required: path: eg: "/fapi/v2/balance"
44
+ optional:
45
+
46
+ - apiKey, secret: can be omitted for public endpoint: eg: "/fapi/v1/exchangeInfo"
47
+ - testnet: 0|1, default: 0
48
+ - data: payload parameters object to be sent
49
+ - raw: 0|1, default: 0, get the raw return with headers and status, else return only the data.
50
+
51
+ ### Parameters 2 (call function)
52
+
53
+ ```
54
+ call(path, { data, apiKey, secret, method, testnet, raw } = {})
55
+ ```
56
+
57
+ required: path: eg: "/papi/v1/balance"
58
+ optional:
59
+
60
+ - data: payload parameters object to be sent
61
+ - apiKey, secret: can be omitted for public endpoint: eg: "/fapi/v1/exchangeInfo"
62
+ - method: "GET|POST", default: "GET"
63
+ - testnet: 0|1, default: 0
64
+ - raw: 0|1, default: 0, get the raw return with headers and status, else return only the data.
65
+
66
+ ### Global rate limit
67
+
68
+ ```
69
+ global.bnbLimit["x-mbx-used-weight"]
70
+ global.bnbLimit["x-mbx-used-weight-1m"]
71
+ global.bnbLimit["x-mbx-order-count-10s"]
72
+ global.bnbLimit["x-mbx-order-count-1m"]
73
+ global.bnbLimit["x-sapi-used-ip-weight-1m"]
74
+ global.bnbLimit["x-sapi-used-uid-weight-1m"]
75
+ ```
76
+
77
+ Only used limit will be shown.
package/index.js ADDED
@@ -0,0 +1,157 @@
1
+ const axios = require("axios");
2
+ // const crypto = require("crypto");
3
+ const hmacSHA256 = require("crypto-js/hmac-sha256");
4
+
5
+ function getSig(data = {}, secret, timestamp = "") {
6
+ let dt = { ...data };
7
+ dt.timestamp ??= timestamp ?? Date.now();
8
+ let qs = new URLSearchParams(dt).toString();
9
+ qs = decodeURIComponent(qs); //convert %40 into @ //qs = qs.replace("/%40/g", "@");
10
+ return hmacSHA256(qs, secret).toString();
11
+ // return crypto.createHmac("sha256", secret).update(qs).digest("hex");
12
+ }
13
+
14
+ function getQs(data = {}, secret = "") {
15
+ let dt = secret ? { ...data, timestamp: Date.now() } : data;
16
+ const qs = new URLSearchParams(dt).toString();
17
+ return secret ? `?${qs}&signature=${getSig(dt, secret)}` : `?${qs}`;
18
+ }
19
+
20
+ function getUrl(path, testnet) {
21
+ let postFix = testnet ? "-testnet" : "";
22
+ if (path.match(/^\/api\//)) path = `https://api.asterdex${postFix}.com` + path;
23
+ else if (path.match(/^\/sapi\//)) path = `https://api.asterdex${postFix}.com` + path;
24
+ else if (path.match(/^\/papi\//)) path = `https://papi.asterdex${postFix}.com` + path;
25
+ else if (path.match(/^\/fapi\//)) path = `https://fapi.asterdex${postFix}.com` + path;
26
+ else if (path.match(/^\/dapi\//)) path = `https://dapi.asterdex${postFix}.com` + path;
27
+ return path;
28
+ }
29
+
30
+ let public = [
31
+ "/api/v3/aggTrades",
32
+ "/api/v3/avgPrice",
33
+ "/api/v3/depth",
34
+ "/api/v3/exchangeInfo",
35
+ "/api/v3/klines",
36
+ "/api/v3/ping",
37
+ "/api/v3/ticker",
38
+ "/api/v3/ticker/24hr",
39
+ "/api/v3/ticker/bookTicker",
40
+ "/api/v3/ticker/price",
41
+ "/api/v3/time",
42
+ "/api/v3/trades",
43
+ "/api/v3/uiKlines",
44
+ ];
45
+
46
+ async function call(path, { data, apiKey, secret, method, testnet, raw } = {}) {
47
+ apiKey ??= "";
48
+ secret ??= "";
49
+ method ??= "GET";
50
+ testnet ??= 0;
51
+ raw ??= 0;
52
+ data ??= {};
53
+ data.recvWindow ??= 30000; // 60s 30s 15s 5s
54
+ let url = testnet ? getUrl(path, true) : getUrl(path);
55
+
56
+ let headers = { "X-MBX-APIKEY": apiKey };
57
+ if (["POST", "DELETE", "PUT"].includes(method)) {
58
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
59
+ if (Object.keys(data).length > 0) {
60
+ let ts = Date.now();
61
+ data.timestamp = ts;
62
+ // data.recvWindow ??= 30000; // 60s 30s 15s 5s
63
+ if (data.signature) delete data.signature; // else same data object sent will have previous signature
64
+ data.signature = getSig(data, secret);
65
+ }
66
+ } else if (method == "PUT") {
67
+ headers["Content-Type"] = "application/x-www-form-urlencoded"; // old backup: maybe PUT cannot be signed for PUT /papi/v1/listenKey
68
+ } else {
69
+ if (public.includes(path)) delete data.recvWindow; // public no recvWindow
70
+ let qs = public.includes(path) ? getQs(data) : getQs(data, secret);
71
+ url += qs;
72
+ data = "";
73
+ }
74
+
75
+ let config = {
76
+ method,
77
+ url,
78
+ headers,
79
+ data,
80
+ };
81
+
82
+ let r = await axios(config)
83
+ .then((r) => {
84
+ let rs = r.data;
85
+ let types = [];
86
+ for (let type in { ...r.headers, ...(global.bnbLimit ?? {}) }) {
87
+ let r = type.match(/^[xX]-(?:mbx|sapi|papi|fapi|api).*$/); // match "x-"
88
+ if (r != null && !types.includes(r[0])) {
89
+ types.push(r[0]);
90
+ }
91
+ }
92
+
93
+ global.bnbLimit ??= {};
94
+ global.bnbLimitTs ??= {};
95
+ for (let type of types) {
96
+ global.bnbLimitTs[type] ??= Date.now();
97
+ let used = r.headers[type] ?? 0;
98
+ if (Date.now() - global.bnbLimitTs[type] > 5000) {
99
+ global.bnbLimitTs[type] = Date.now();
100
+ global.bnbLimit[type] = 0; // reset every 5s
101
+ }
102
+ if (!isNaN(used)) {
103
+ global.bnbLimit[type] ??= Number(used);
104
+ global.bnbLimit[type] = Math.max(global.bnbLimit[type], used);
105
+ } else global.bnbLimit[type] ??= used; // x-mbx-uuid
106
+ }
107
+ return raw ? r : rs;
108
+ })
109
+ .catch(function (e) {
110
+ console.log(e?.response?.data);
111
+ return e?.response?.data; // { code: 51999, msg: 'System abnormality.' }
112
+ });
113
+ return r;
114
+ }
115
+
116
+ class api {
117
+ constructor(apiKey = "", secret = "", { testnet } = {}) {
118
+ this.apiKey = apiKey;
119
+ this.secret = secret;
120
+ this.testnet = testnet ?? 0;
121
+ }
122
+ init(data = {}) {
123
+ return { data, apiKey: this.apiKey, secret: this.secret, testnet: this.testnet };
124
+ }
125
+ async get(path, data = {}, { raw } = {}) {
126
+ let dt = this.init(data);
127
+ dt.raw = raw ?? 0;
128
+ return call(path, dt);
129
+ }
130
+ async post(path, data = {}, { raw } = {}) {
131
+ let dt = this.init(data);
132
+ dt.method = "POST";
133
+ dt.raw = raw ?? 0;
134
+ return call(path, dt);
135
+ }
136
+ async put(path, data = {}, { raw } = {}) {
137
+ let dt = this.init(data);
138
+ dt.method = "PUT";
139
+ dt.raw = raw ?? 0;
140
+ return call(path, dt);
141
+ }
142
+ async delete(path, data = {}, { raw } = {}) {
143
+ let dt = this.init(data);
144
+ dt.method = "DELETE";
145
+ dt.raw = raw ?? 0;
146
+ return call(path, dt);
147
+ }
148
+ async getPublic(path, data = {}, { raw } = {}) {
149
+ let dt = this.init(data);
150
+ dt.secret = "";
151
+ dt.raw = raw ?? 0;
152
+ return call(path, dt);
153
+ }
154
+ }
155
+
156
+ module.exports = api;
157
+ module.exports.call = call;
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "asterdex",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [
10
+ "aster",
11
+ "api"
12
+ ],
13
+ "author": "",
14
+ "license": "ISC",
15
+ "dependencies": {
16
+ "axios": "^1.13.2",
17
+ "crypto-js": "^4.2.0"
18
+ }
19
+ }