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.
- package/README.md +77 -0
- package/index.js +157 -0
- 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
|
+
}
|