@fenelabs/fene-sdk 0.2.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/CHANGELOG.md +152 -0
- package/README.md +429 -0
- package/dist/index.d.mts +937 -0
- package/dist/index.d.ts +937 -0
- package/dist/index.js +989 -0
- package/dist/index.mjs +954 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,989 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AnalyticsAPI: () => AnalyticsAPI,
|
|
24
|
+
AuthService: () => AuthService,
|
|
25
|
+
DelegatorsAPI: () => DelegatorsAPI,
|
|
26
|
+
GlobalAPI: () => GlobalAPI,
|
|
27
|
+
HTTPClient: () => HTTPClient,
|
|
28
|
+
ReferralsAPI: () => ReferralsAPI,
|
|
29
|
+
ResonanceSDK: () => ResonanceSDK,
|
|
30
|
+
SlashingAPI: () => SlashingAPI,
|
|
31
|
+
ValidatorsAPI: () => ValidatorsAPI,
|
|
32
|
+
default: () => index_default
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// src/errors.ts
|
|
37
|
+
var ResonanceAPIError = class extends Error {
|
|
38
|
+
constructor(statusCode, code, message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.statusCode = statusCode;
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.name = "ResonanceAPIError";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/client.ts
|
|
47
|
+
var HTTPClient = class {
|
|
48
|
+
constructor(config) {
|
|
49
|
+
this.baseUrl = config.apiUrl.replace(/\/$/, "");
|
|
50
|
+
this.timeout = config.timeout || 3e4;
|
|
51
|
+
this.headers = {
|
|
52
|
+
"Content-Type": "application/json",
|
|
53
|
+
...config.headers
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build URL with query parameters
|
|
58
|
+
*/
|
|
59
|
+
buildUrl(path, params) {
|
|
60
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
61
|
+
if (params) {
|
|
62
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
63
|
+
if (value !== void 0 && value !== null) {
|
|
64
|
+
url.searchParams.append(key, String(value));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return url.toString();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Set authorization token
|
|
72
|
+
*/
|
|
73
|
+
setAuthToken(token) {
|
|
74
|
+
if (token) {
|
|
75
|
+
this.headers["Authorization"] = `Bearer ${token}`;
|
|
76
|
+
} else {
|
|
77
|
+
delete this.headers["Authorization"];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Set custom header
|
|
82
|
+
*/
|
|
83
|
+
setHeader(key, value) {
|
|
84
|
+
this.headers[key] = value;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Make HTTP GET request
|
|
88
|
+
*/
|
|
89
|
+
async get(path, params) {
|
|
90
|
+
const url = this.buildUrl(path, params);
|
|
91
|
+
const controller = new AbortController();
|
|
92
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch(url, {
|
|
95
|
+
method: "GET",
|
|
96
|
+
headers: this.headers,
|
|
97
|
+
signal: controller.signal
|
|
98
|
+
});
|
|
99
|
+
clearTimeout(timeoutId);
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
await this.handleError(response);
|
|
102
|
+
}
|
|
103
|
+
const json = await response.json();
|
|
104
|
+
return json.data !== void 0 ? json.data : json;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
107
|
+
throw new Error(`Request timeout after ${this.timeout}ms`);
|
|
108
|
+
}
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Make HTTP POST request
|
|
114
|
+
*/
|
|
115
|
+
async post(path, body) {
|
|
116
|
+
const url = this.buildUrl(path);
|
|
117
|
+
const controller = new AbortController();
|
|
118
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
119
|
+
try {
|
|
120
|
+
const response = await fetch(url, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: this.headers,
|
|
123
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
124
|
+
signal: controller.signal
|
|
125
|
+
});
|
|
126
|
+
clearTimeout(timeoutId);
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
await this.handleError(response);
|
|
129
|
+
}
|
|
130
|
+
const json = await response.json();
|
|
131
|
+
return json.data !== void 0 ? json.data : json;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
134
|
+
throw new Error(`Request timeout after ${this.timeout}ms`);
|
|
135
|
+
}
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Make HTTP DELETE request
|
|
141
|
+
*/
|
|
142
|
+
async delete(path) {
|
|
143
|
+
const url = this.buildUrl(path);
|
|
144
|
+
const controller = new AbortController();
|
|
145
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch(url, {
|
|
148
|
+
method: "DELETE",
|
|
149
|
+
headers: this.headers,
|
|
150
|
+
signal: controller.signal
|
|
151
|
+
});
|
|
152
|
+
clearTimeout(timeoutId);
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
await this.handleError(response);
|
|
155
|
+
}
|
|
156
|
+
const json = await response.json();
|
|
157
|
+
return json.data !== void 0 ? json.data : json;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
160
|
+
throw new Error(`Request timeout after ${this.timeout}ms`);
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Handle HTTP errors
|
|
167
|
+
*/
|
|
168
|
+
async handleError(response) {
|
|
169
|
+
let errorCode = "UNKNOWN_ERROR";
|
|
170
|
+
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
|
|
171
|
+
try {
|
|
172
|
+
const errorData = await response.json();
|
|
173
|
+
if (errorData.error) {
|
|
174
|
+
errorCode = errorData.error.code;
|
|
175
|
+
errorMessage = errorData.error.message;
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
throw new ResonanceAPIError(response.status, errorCode, errorMessage);
|
|
180
|
+
}
|
|
181
|
+
/*
|
|
182
|
+
* Automatic Retry transient errors network
|
|
183
|
+
*/
|
|
184
|
+
async fetchWithRetry(url, options, retries = 3) {
|
|
185
|
+
for (let i = 0; i < retries; i++) {
|
|
186
|
+
try {
|
|
187
|
+
return await fetch(url, options);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (i === retries - 1) throw error;
|
|
190
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * (i + 1)));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
throw new Error("Max retries reached");
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// src/auth.ts
|
|
198
|
+
var AuthService = class {
|
|
199
|
+
constructor(apiUrl) {
|
|
200
|
+
this.tokenKey = "resonance_jwt_token";
|
|
201
|
+
this.apiUrl = apiUrl;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get nonce for wallet authentication
|
|
205
|
+
* POST /eth/v1/auth/nonce
|
|
206
|
+
*/
|
|
207
|
+
async getNonce(address) {
|
|
208
|
+
const response = await fetch(`${this.apiUrl}/eth/v1/auth/nonce`, {
|
|
209
|
+
method: "POST",
|
|
210
|
+
headers: { "Content-Type": "application/json" },
|
|
211
|
+
body: JSON.stringify({ address })
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
throw new Error("Failed to get nonce");
|
|
215
|
+
}
|
|
216
|
+
const result = await response.json();
|
|
217
|
+
return result.data;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Verify wallet signature and get JWT token
|
|
221
|
+
* POST /eth/v1/auth/verify
|
|
222
|
+
*/
|
|
223
|
+
async verify(params) {
|
|
224
|
+
const response = await fetch(`${this.apiUrl}/eth/v1/auth/verify`, {
|
|
225
|
+
method: "POST",
|
|
226
|
+
headers: { "Content-Type": "application/json" },
|
|
227
|
+
body: JSON.stringify(params)
|
|
228
|
+
});
|
|
229
|
+
if (!response.ok) {
|
|
230
|
+
throw new Error("Authentication failed");
|
|
231
|
+
}
|
|
232
|
+
const result = await response.json();
|
|
233
|
+
const token = result.data.token;
|
|
234
|
+
if (typeof window !== "undefined") {
|
|
235
|
+
sessionStorage.setItem(this.tokenKey, token);
|
|
236
|
+
}
|
|
237
|
+
return { token };
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Connect wallet and authenticate
|
|
241
|
+
*/
|
|
242
|
+
async connectWallet(provider, role = "delegator") {
|
|
243
|
+
const signer = await provider.getSigner();
|
|
244
|
+
const address = await signer.getAddress();
|
|
245
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
246
|
+
const nonce = Math.floor(Math.random() * 1e6);
|
|
247
|
+
const message = `Sign this message to login to Resonance Dashboard
|
|
248
|
+
|
|
249
|
+
Address: ${address}
|
|
250
|
+
Role: ${role}
|
|
251
|
+
Nonce: ${nonce}
|
|
252
|
+
Timestamp: ${timestamp}
|
|
253
|
+
|
|
254
|
+
This will not trigger any blockchain transaction or cost gas fees.`;
|
|
255
|
+
const signature = await signer.signMessage(message);
|
|
256
|
+
const response = await fetch(`${this.apiUrl}/auth/login`, {
|
|
257
|
+
method: "POST",
|
|
258
|
+
headers: { "Content-Type": "application/json" },
|
|
259
|
+
body: JSON.stringify({
|
|
260
|
+
address,
|
|
261
|
+
message,
|
|
262
|
+
signature,
|
|
263
|
+
role,
|
|
264
|
+
timestamp,
|
|
265
|
+
nonce
|
|
266
|
+
})
|
|
267
|
+
});
|
|
268
|
+
if (!response.ok) {
|
|
269
|
+
throw new Error("Authentication failed");
|
|
270
|
+
}
|
|
271
|
+
const data = await response.json();
|
|
272
|
+
if (typeof window !== "undefined") {
|
|
273
|
+
sessionStorage.setItem(this.tokenKey, data.token);
|
|
274
|
+
}
|
|
275
|
+
return data;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get stored JWT token
|
|
279
|
+
*/
|
|
280
|
+
getToken() {
|
|
281
|
+
if (typeof window === "undefined") return null;
|
|
282
|
+
return sessionStorage.getItem(this.tokenKey);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Check if user is authenticated
|
|
286
|
+
*/
|
|
287
|
+
isAuthenticated() {
|
|
288
|
+
const token = this.getToken();
|
|
289
|
+
if (!token) return false;
|
|
290
|
+
try {
|
|
291
|
+
const claims = this.parseToken(token);
|
|
292
|
+
return claims.exp * 1e3 > Date.now();
|
|
293
|
+
} catch {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Parse JWT token to get claims
|
|
299
|
+
*/
|
|
300
|
+
parseToken(token) {
|
|
301
|
+
const payload = token.split(".")[1];
|
|
302
|
+
const decoded = atob(payload);
|
|
303
|
+
return JSON.parse(decoded);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Get user info from token
|
|
307
|
+
*/
|
|
308
|
+
getUserInfo() {
|
|
309
|
+
const token = this.getToken();
|
|
310
|
+
if (!token) return null;
|
|
311
|
+
try {
|
|
312
|
+
const claims = this.parseToken(token);
|
|
313
|
+
return {
|
|
314
|
+
address: claims.address,
|
|
315
|
+
role: claims.role
|
|
316
|
+
};
|
|
317
|
+
} catch {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Logout user
|
|
323
|
+
*/
|
|
324
|
+
logout() {
|
|
325
|
+
if (typeof window !== "undefined") {
|
|
326
|
+
sessionStorage.removeItem(this.tokenKey);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Check if token is expiring soon (within 5 minutes)
|
|
331
|
+
*/
|
|
332
|
+
isTokenExpiringSoon() {
|
|
333
|
+
const token = this.getToken();
|
|
334
|
+
if (!token) return true;
|
|
335
|
+
try {
|
|
336
|
+
const claims = this.parseToken(token);
|
|
337
|
+
const expiresAt = claims.exp * 1e3;
|
|
338
|
+
const fiveMinutes = 5 * 60 * 1e3;
|
|
339
|
+
return expiresAt - Date.now() < fiveMinutes;
|
|
340
|
+
} catch {
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// src/validators.ts
|
|
347
|
+
var ValidatorsAPI = class {
|
|
348
|
+
constructor(client) {
|
|
349
|
+
this.client = client;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Get all validators
|
|
353
|
+
* GET /eth/v1/validators
|
|
354
|
+
*/
|
|
355
|
+
async getAll() {
|
|
356
|
+
return this.client.get("/eth/v1/validators");
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Get validator details
|
|
360
|
+
* GET /eth/v1/validators/:address
|
|
361
|
+
*/
|
|
362
|
+
async get(address) {
|
|
363
|
+
return this.client.get(`/eth/v1/validators/${address}`);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Get validator delegators
|
|
367
|
+
* GET /eth/v1/validators/:address/delegators
|
|
368
|
+
*/
|
|
369
|
+
async getDelegators(address) {
|
|
370
|
+
return this.client.get(`/eth/v1/validators/${address}/delegators`);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Get validator stake breakdown
|
|
374
|
+
* GET /eth/v1/validators/:address/stake
|
|
375
|
+
*/
|
|
376
|
+
async getStakeBreakdown(address) {
|
|
377
|
+
return this.client.get(`/eth/v1/validators/${address}/stake`);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Get validator epoch details
|
|
381
|
+
* GET /eth/v1/validators/:address/epochs/:epoch
|
|
382
|
+
*/
|
|
383
|
+
async getEpoch(address, epoch) {
|
|
384
|
+
return this.client.get(`/eth/v1/validators/${address}/epochs/${epoch}`);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get validator history
|
|
388
|
+
* GET /eth/v1/validators/:address/history
|
|
389
|
+
* @param address - Validator address
|
|
390
|
+
* @param fromEpoch - Optional starting epoch
|
|
391
|
+
* @param toEpoch - Optional ending epoch
|
|
392
|
+
*/
|
|
393
|
+
async getHistory(address, fromEpoch, toEpoch) {
|
|
394
|
+
const params = {};
|
|
395
|
+
if (fromEpoch !== void 0) params.from_epoch = fromEpoch;
|
|
396
|
+
if (toEpoch !== void 0) params.to_epoch = toEpoch;
|
|
397
|
+
return this.client.get(
|
|
398
|
+
`/eth/v1/validators/${address}/history`,
|
|
399
|
+
params
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Get validator withdrawals
|
|
404
|
+
* GET /eth/v1/validators/:address/withdrawals
|
|
405
|
+
* @param address - Validator address
|
|
406
|
+
* @param limit - Optional limit
|
|
407
|
+
* @param offset - Optional offset
|
|
408
|
+
*/
|
|
409
|
+
async getWithdrawals(address, limit, offset) {
|
|
410
|
+
const params = {};
|
|
411
|
+
if (limit !== void 0) params.limit = limit;
|
|
412
|
+
if (offset !== void 0) params.offset = offset;
|
|
413
|
+
return this.client.get(
|
|
414
|
+
`/eth/v1/validators/${address}/withdrawals`,
|
|
415
|
+
params
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Get validator metrics
|
|
420
|
+
* GET /eth/v1/validators/:address/metrics
|
|
421
|
+
*/
|
|
422
|
+
async getMetrics(address) {
|
|
423
|
+
return this.client.get(`/eth/v1/validators/${address}/metrics`);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Get validator slashing events
|
|
427
|
+
* GET /eth/v1/validators/:address/slashing
|
|
428
|
+
*/
|
|
429
|
+
async getSlashing(address) {
|
|
430
|
+
return this.client.get(`/eth/v1/validators/${address}/slashing`);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Get validator APR
|
|
434
|
+
* GET /eth/v1/validators/:address/apr
|
|
435
|
+
*/
|
|
436
|
+
async getAPR(address) {
|
|
437
|
+
return this.client.get(`/eth/v1/validators/${address}/apr`);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// src/delegators.ts
|
|
442
|
+
var DelegatorsAPI = class {
|
|
443
|
+
constructor(client) {
|
|
444
|
+
this.client = client;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Get all delegators
|
|
448
|
+
* GET /eth/v1/delegators
|
|
449
|
+
*/
|
|
450
|
+
async getAll() {
|
|
451
|
+
return this.client.get("/eth/v1/delegators");
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Get delegator details
|
|
455
|
+
* GET /eth/v1/delegators/:address
|
|
456
|
+
*/
|
|
457
|
+
async get(address) {
|
|
458
|
+
return this.client.get(`/eth/v1/delegators/${address}`);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Get delegator stakes breakdown per validator
|
|
462
|
+
* GET /eth/v1/delegators/:address/stakes
|
|
463
|
+
*/
|
|
464
|
+
async getStakes(address) {
|
|
465
|
+
return this.client.get(`/eth/v1/delegators/${address}/stakes`);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Get delegator rewards history
|
|
469
|
+
* GET /eth/v1/delegators/:address/rewards
|
|
470
|
+
* @param address - Delegator address
|
|
471
|
+
* @param limit - Optional limit
|
|
472
|
+
* @param offset - Optional offset
|
|
473
|
+
*/
|
|
474
|
+
async getRewards(address, limit, offset) {
|
|
475
|
+
const params = {};
|
|
476
|
+
if (limit !== void 0) params.limit = limit;
|
|
477
|
+
if (offset !== void 0) params.offset = offset;
|
|
478
|
+
return this.client.get(
|
|
479
|
+
`/eth/v1/delegators/${address}/rewards`,
|
|
480
|
+
params
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Get delegator withdrawals history
|
|
485
|
+
* GET /eth/v1/delegators/:address/withdrawals
|
|
486
|
+
* @param address - Delegator address
|
|
487
|
+
* @param limit - Optional limit
|
|
488
|
+
* @param offset - Optional offset
|
|
489
|
+
*/
|
|
490
|
+
async getWithdrawals(address, limit, offset) {
|
|
491
|
+
const params = {};
|
|
492
|
+
if (limit !== void 0) params.limit = limit;
|
|
493
|
+
if (offset !== void 0) params.offset = offset;
|
|
494
|
+
return this.client.get(
|
|
495
|
+
`/eth/v1/delegators/${address}/withdrawals`,
|
|
496
|
+
params
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Get delegator unbonding status
|
|
501
|
+
* GET /eth/v1/delegators/:address/unbonding
|
|
502
|
+
*/
|
|
503
|
+
async getUnbonding(address) {
|
|
504
|
+
return this.client.get(`/eth/v1/delegators/${address}/unbonding`);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Get delegator active validators
|
|
508
|
+
* GET /eth/v1/delegators/:address/validators
|
|
509
|
+
*/
|
|
510
|
+
async getValidators(address) {
|
|
511
|
+
return this.client.get(`/eth/v1/delegators/${address}/validators`);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// src/referrals.ts
|
|
516
|
+
var ReferralsAPI = class {
|
|
517
|
+
constructor(client) {
|
|
518
|
+
this.client = client;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Validate a referral code
|
|
522
|
+
* GET /eth/v1/referrals/validate?code=CODE
|
|
523
|
+
*/
|
|
524
|
+
async validate(referralCode) {
|
|
525
|
+
return this.client.get("/eth/v1/referrals/validate", {
|
|
526
|
+
code: referralCode
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Create a new referral code
|
|
531
|
+
* POST /eth/v1/referrals
|
|
532
|
+
* @param request - Referral creation request
|
|
533
|
+
*/
|
|
534
|
+
async create(request) {
|
|
535
|
+
return this.client.post("/eth/v1/referrals", request);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Apply a referral code
|
|
539
|
+
* POST /eth/v1/referrals/apply
|
|
540
|
+
* @param request - Referral application request
|
|
541
|
+
*/
|
|
542
|
+
async apply(request) {
|
|
543
|
+
return this.client.post("/eth/v1/referrals/apply", request);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Delete a referral code
|
|
547
|
+
* DELETE /eth/v1/referrals/:referral_code
|
|
548
|
+
* @param referralCode - The referral code to delete
|
|
549
|
+
*/
|
|
550
|
+
async delete(referralCode) {
|
|
551
|
+
return this.client.delete(`/eth/v1/referrals/${referralCode}`);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Unlink a delegator from referral
|
|
555
|
+
* DELETE /eth/v1/referrals/unlink/:delegator_address
|
|
556
|
+
* @param delegatorAddress - The delegator address to unlink
|
|
557
|
+
*/
|
|
558
|
+
async unlink(delegatorAddress) {
|
|
559
|
+
return this.client.delete(`/eth/v1/referrals/unlink/${delegatorAddress}`);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Get delegator referral information
|
|
563
|
+
* GET /eth/v1/referrals/delegators/:delegator_address
|
|
564
|
+
*/
|
|
565
|
+
async getDelegatorReferral(delegatorAddress) {
|
|
566
|
+
return this.client.get(
|
|
567
|
+
`/eth/v1/referrals/delegators/${delegatorAddress}`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Get validator referral information
|
|
572
|
+
* GET /eth/v1/referrals/validators/:validator_address
|
|
573
|
+
*/
|
|
574
|
+
async getValidatorReferral(validatorAddress) {
|
|
575
|
+
return this.client.get(
|
|
576
|
+
`/eth/v1/referrals/validators/${validatorAddress}`
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// src/global.ts
|
|
582
|
+
var GlobalAPI = class {
|
|
583
|
+
constructor(client) {
|
|
584
|
+
this.client = client;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Get global network statistics
|
|
588
|
+
* GET /eth/v1/global
|
|
589
|
+
*/
|
|
590
|
+
async getStats() {
|
|
591
|
+
return this.client.get("/eth/v1/global");
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Get network APR (from /global endpoint)
|
|
595
|
+
* GET /eth/v1/global/network_apr
|
|
596
|
+
*/
|
|
597
|
+
async getNetworkAPRFromGlobal() {
|
|
598
|
+
return this.client.get("/eth/v1/global/network_apr");
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Get network APR (from /network endpoint)
|
|
602
|
+
* GET /eth/v1/network/apr
|
|
603
|
+
*/
|
|
604
|
+
async getNetworkAPR() {
|
|
605
|
+
return this.client.get("/eth/v1/network/apr");
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Get network APR breakdown
|
|
609
|
+
* GET /eth/v1/network/apr/breakdown
|
|
610
|
+
*/
|
|
611
|
+
async getNetworkAPRBreakdown() {
|
|
612
|
+
return this.client.get("/eth/v1/network/apr/breakdown");
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Get delegator leaderboard
|
|
616
|
+
* GET /eth/v1/leaderboard/delegators
|
|
617
|
+
* @param limit - Optional limit (default backend value)
|
|
618
|
+
* @param offset - Optional offset for pagination
|
|
619
|
+
*/
|
|
620
|
+
async getLeaderboardDelegators(limit, offset) {
|
|
621
|
+
const params = {};
|
|
622
|
+
if (limit !== void 0) params.limit = limit;
|
|
623
|
+
if (offset !== void 0) params.offset = offset;
|
|
624
|
+
return this.client.get(
|
|
625
|
+
"/eth/v1/leaderboard/delegators",
|
|
626
|
+
params
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Get validator leaderboard
|
|
631
|
+
* GET /eth/v1/leaderboard/validators
|
|
632
|
+
* @param limit - Optional limit (default backend value)
|
|
633
|
+
* @param offset - Optional offset for pagination
|
|
634
|
+
*/
|
|
635
|
+
async getLeaderboardValidators(limit, offset) {
|
|
636
|
+
const params = {};
|
|
637
|
+
if (limit !== void 0) params.limit = limit;
|
|
638
|
+
if (offset !== void 0) params.offset = offset;
|
|
639
|
+
return this.client.get(
|
|
640
|
+
"/eth/v1/leaderboard/validators",
|
|
641
|
+
params
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
// src/slashing.ts
|
|
647
|
+
var SlashingAPI = class {
|
|
648
|
+
constructor(client) {
|
|
649
|
+
this.client = client;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Get slashing events
|
|
653
|
+
* GET /eth/v1/slashing/events
|
|
654
|
+
* @param validatorAddress - Optional validator address filter
|
|
655
|
+
* @param limit - Optional limit
|
|
656
|
+
* @param offset - Optional offset
|
|
657
|
+
*/
|
|
658
|
+
async getEvents(validatorAddress, limit, offset) {
|
|
659
|
+
const params = {};
|
|
660
|
+
if (validatorAddress) params.validator_address = validatorAddress;
|
|
661
|
+
if (limit !== void 0) params.limit = limit;
|
|
662
|
+
if (offset !== void 0) params.offset = offset;
|
|
663
|
+
return this.client.get("/eth/v1/slashing/events", params);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// src/modules/analytics.ts
|
|
668
|
+
var AnalyticsAPI = class {
|
|
669
|
+
constructor(client) {
|
|
670
|
+
this.basePath = "/eth/v1/analytics";
|
|
671
|
+
this.client = client;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Get protocol-level statistics
|
|
675
|
+
*
|
|
676
|
+
* Returns aggregated statistics about the entire protocol including
|
|
677
|
+
* total staking, validator counts, and rewards distribution.
|
|
678
|
+
*
|
|
679
|
+
* @returns Protocol statistics from subgraph
|
|
680
|
+
*
|
|
681
|
+
* @example
|
|
682
|
+
* ```typescript
|
|
683
|
+
* const stats = await sdk.analytics.getProtocolStats();
|
|
684
|
+
* console.log(`Total Staking: ${stats.TotalStaking}`);
|
|
685
|
+
* console.log(`Active Validators: ${stats.ActiveValidators}`);
|
|
686
|
+
* ```
|
|
687
|
+
*/
|
|
688
|
+
async getProtocolStats() {
|
|
689
|
+
return this.client.get(`${this.basePath}/protocol`);
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Get sync status of analytics workers
|
|
693
|
+
*
|
|
694
|
+
* Returns the current synchronization status of background workers
|
|
695
|
+
* that index blockchain data into the analytics database.
|
|
696
|
+
*
|
|
697
|
+
* @returns Sync status for each worker
|
|
698
|
+
*
|
|
699
|
+
* @example
|
|
700
|
+
* ```typescript
|
|
701
|
+
* const status = await sdk.analytics.getSyncStatus();
|
|
702
|
+
* if (status.protocol_sync.status === 'success') {
|
|
703
|
+
* console.log(`Last synced at block: ${status.protocol_sync.last_block}`);
|
|
704
|
+
* }
|
|
705
|
+
* ```
|
|
706
|
+
*/
|
|
707
|
+
async getSyncStatus() {
|
|
708
|
+
return this.client.get(`${this.basePath}/sync-status`);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Get all validators with analytics data
|
|
712
|
+
*
|
|
713
|
+
* Returns a paginated list of validators with their analytics metrics
|
|
714
|
+
* including uptime, signed blocks, and staker counts.
|
|
715
|
+
*
|
|
716
|
+
* @param options - Pagination and filter options
|
|
717
|
+
* @returns Paginated list of validators
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* ```typescript
|
|
721
|
+
* // Get first 20 validators
|
|
722
|
+
* const result = await sdk.analytics.getAllValidators({ limit: 20 });
|
|
723
|
+
*
|
|
724
|
+
* // Get next page
|
|
725
|
+
* const nextPage = await sdk.analytics.getAllValidators({
|
|
726
|
+
* limit: 20,
|
|
727
|
+
* offset: 20
|
|
728
|
+
* });
|
|
729
|
+
*
|
|
730
|
+
* // Filter by status
|
|
731
|
+
* const active = await sdk.analytics.getAllValidators({
|
|
732
|
+
* status: 'active',
|
|
733
|
+
* limit: 50
|
|
734
|
+
* });
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
async getAllValidators(options) {
|
|
738
|
+
const params = {};
|
|
739
|
+
if (options?.limit) params.limit = options.limit;
|
|
740
|
+
if (options?.offset) params.offset = options.offset;
|
|
741
|
+
if (options?.status) params.status = options.status;
|
|
742
|
+
const response = await this.client.get(
|
|
743
|
+
`${this.basePath}/validators`,
|
|
744
|
+
params
|
|
745
|
+
);
|
|
746
|
+
if (Array.isArray(response)) {
|
|
747
|
+
return {
|
|
748
|
+
count: response.length,
|
|
749
|
+
data: response
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
return response;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Get top validators by uptime
|
|
756
|
+
*
|
|
757
|
+
* Returns validators sorted by uptime percentage in descending order.
|
|
758
|
+
* Useful for displaying leaderboards or finding most reliable validators.
|
|
759
|
+
*
|
|
760
|
+
* @param limit - Maximum number of validators to return (default: 10)
|
|
761
|
+
* @returns Top validators by uptime
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* ```typescript
|
|
765
|
+
* // Get top 5 validators
|
|
766
|
+
* const top5 = await sdk.analytics.getTopValidators(5);
|
|
767
|
+
*
|
|
768
|
+
* top5.data.forEach((validator, index) => {
|
|
769
|
+
* console.log(`#${index + 1}: ${validator.moniker} - ${validator.uptime}% uptime`);
|
|
770
|
+
* });
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
async getTopValidators(limit = 10) {
|
|
774
|
+
const response = await this.client.get(
|
|
775
|
+
`${this.basePath}/validators/top`,
|
|
776
|
+
{ limit }
|
|
777
|
+
);
|
|
778
|
+
if (Array.isArray(response)) {
|
|
779
|
+
return {
|
|
780
|
+
count: response.length,
|
|
781
|
+
data: response
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
return response;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Get analytics data for a specific validator
|
|
788
|
+
*
|
|
789
|
+
* Returns detailed analytics metrics for a single validator including
|
|
790
|
+
* performance statistics and current status.
|
|
791
|
+
*
|
|
792
|
+
* @param address - Validator address (with or without 0x prefix)
|
|
793
|
+
* @returns Validator analytics data
|
|
794
|
+
*
|
|
795
|
+
* @throws {ResonanceAPIError} 404 if validator not found
|
|
796
|
+
*
|
|
797
|
+
* @example
|
|
798
|
+
* ```typescript
|
|
799
|
+
* const validator = await sdk.analytics.getValidatorAnalytics('0x1234...');
|
|
800
|
+
* console.log(`${validator.moniker}: ${validator.uptime}% uptime`);
|
|
801
|
+
* console.log(`Signed: ${validator.signed_blocks}, Missed: ${validator.missed_blocks}`);
|
|
802
|
+
* ```
|
|
803
|
+
*/
|
|
804
|
+
async getValidatorAnalytics(address) {
|
|
805
|
+
const cleanAddress = address.toLowerCase().replace(/^0x/, "");
|
|
806
|
+
return this.client.get(
|
|
807
|
+
`${this.basePath}/validators/0x${cleanAddress}`
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Get validator rewards with pagination
|
|
812
|
+
*
|
|
813
|
+
* Returns detailed reward and stake information for a validator.
|
|
814
|
+
* This is a heavy endpoint that MUST use pagination.
|
|
815
|
+
*
|
|
816
|
+
* @param address - Validator address (with or without 0x prefix)
|
|
817
|
+
* @param options - Pagination options (required)
|
|
818
|
+
* @returns Validator rewards, stakes, and summary
|
|
819
|
+
*
|
|
820
|
+
* @throws {ResonanceAPIError} 404 if validator not found
|
|
821
|
+
*
|
|
822
|
+
* @remarks
|
|
823
|
+
* This endpoint can return large amounts of data. Always use pagination
|
|
824
|
+
* with reasonable limit values (recommended: 50-100).
|
|
825
|
+
*
|
|
826
|
+
* @example
|
|
827
|
+
* ```typescript
|
|
828
|
+
* // Get first page of rewards
|
|
829
|
+
* const page1 = await sdk.analytics.getValidatorRewards('0x1234...', {
|
|
830
|
+
* limit: 50,
|
|
831
|
+
* offset: 0
|
|
832
|
+
* });
|
|
833
|
+
*
|
|
834
|
+
* console.log(`Total stakers: ${page1.summary.total_stakers}`);
|
|
835
|
+
* console.log(`Has more data: ${page1.metadata.has_more}`);
|
|
836
|
+
*
|
|
837
|
+
* // Get next page if available
|
|
838
|
+
* if (page1.metadata.has_more) {
|
|
839
|
+
* const page2 = await sdk.analytics.getValidatorRewards('0x1234...', {
|
|
840
|
+
* limit: 50,
|
|
841
|
+
* offset: 50
|
|
842
|
+
* });
|
|
843
|
+
* }
|
|
844
|
+
* ```
|
|
845
|
+
*/
|
|
846
|
+
async getValidatorRewards(address, options) {
|
|
847
|
+
const cleanAddress = address.toLowerCase().replace(/^0x/, "");
|
|
848
|
+
const limit = options.limit || 50;
|
|
849
|
+
const offset = options.offset || 0;
|
|
850
|
+
return this.client.get(
|
|
851
|
+
`${this.basePath}/validators/0x${cleanAddress}/rewards`,
|
|
852
|
+
{ limit, offset }
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Get all validator rewards with automatic pagination
|
|
857
|
+
*
|
|
858
|
+
* Automatically fetches all pages of validator rewards data.
|
|
859
|
+
* Use with caution as this can make multiple API calls.
|
|
860
|
+
*
|
|
861
|
+
* @param address - Validator address (with or without 0x prefix)
|
|
862
|
+
* @param options - Batch size and safety limits
|
|
863
|
+
* @returns Complete validator rewards data
|
|
864
|
+
*
|
|
865
|
+
* @throws {ResonanceAPIError} 404 if validator not found
|
|
866
|
+
* @throws {Error} If max pages limit is reached
|
|
867
|
+
*
|
|
868
|
+
* @remarks
|
|
869
|
+
* This method will make multiple API calls. Use maxPages to prevent
|
|
870
|
+
* infinite loops or excessive API usage.
|
|
871
|
+
*
|
|
872
|
+
* @example
|
|
873
|
+
* ```typescript
|
|
874
|
+
* // Fetch all rewards with default settings
|
|
875
|
+
* const allRewards = await sdk.analytics.getAllValidatorRewards('0x1234...');
|
|
876
|
+
*
|
|
877
|
+
* // Custom batch size and limit
|
|
878
|
+
* const rewards = await sdk.analytics.getAllValidatorRewards('0x1234...', {
|
|
879
|
+
* batchSize: 100,
|
|
880
|
+
* maxPages: 5
|
|
881
|
+
* });
|
|
882
|
+
*
|
|
883
|
+
* console.log(`Total rewards: ${rewards.summary.total_rewards}`);
|
|
884
|
+
* console.log(`Total API calls made: ${Math.ceil(rewards.stakes.length / 100)}`);
|
|
885
|
+
* ```
|
|
886
|
+
*/
|
|
887
|
+
async getAllValidatorRewards(address, options) {
|
|
888
|
+
const batchSize = options?.batchSize || 50;
|
|
889
|
+
const maxPages = options?.maxPages || 10;
|
|
890
|
+
let offset = 0;
|
|
891
|
+
let page = 0;
|
|
892
|
+
let firstResponse = null;
|
|
893
|
+
const allRewards = [];
|
|
894
|
+
const allStakes = [];
|
|
895
|
+
while (page < maxPages) {
|
|
896
|
+
const response = await this.getValidatorRewards(address, {
|
|
897
|
+
limit: batchSize,
|
|
898
|
+
offset
|
|
899
|
+
});
|
|
900
|
+
if (!firstResponse) {
|
|
901
|
+
firstResponse = response;
|
|
902
|
+
}
|
|
903
|
+
allRewards.push(...response.rewards);
|
|
904
|
+
allStakes.push(...response.stakes);
|
|
905
|
+
if (!response.metadata.has_more) {
|
|
906
|
+
break;
|
|
907
|
+
}
|
|
908
|
+
offset += batchSize;
|
|
909
|
+
page++;
|
|
910
|
+
}
|
|
911
|
+
if (page >= maxPages && firstResponse?.metadata.has_more) {
|
|
912
|
+
throw new Error(
|
|
913
|
+
`Reached maximum page limit (${maxPages}). Use manual pagination for more control.`
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
return {
|
|
917
|
+
...firstResponse,
|
|
918
|
+
rewards: allRewards,
|
|
919
|
+
stakes: allStakes,
|
|
920
|
+
metadata: {
|
|
921
|
+
...firstResponse.metadata,
|
|
922
|
+
has_more: false,
|
|
923
|
+
limit: allRewards.length,
|
|
924
|
+
offset: 0
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
// src/index.ts
|
|
931
|
+
var ResonanceSDK = class {
|
|
932
|
+
constructor(config) {
|
|
933
|
+
this.client = new HTTPClient(config);
|
|
934
|
+
this.auth = new AuthService(config.apiUrl);
|
|
935
|
+
this.validators = new ValidatorsAPI(this.client);
|
|
936
|
+
this.delegators = new DelegatorsAPI(this.client);
|
|
937
|
+
this.referrals = new ReferralsAPI(this.client);
|
|
938
|
+
this.global = new GlobalAPI(this.client);
|
|
939
|
+
this.slashing = new SlashingAPI(this.client);
|
|
940
|
+
this.analytics = new AnalyticsAPI(this.client);
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Set authentication token for API requests
|
|
944
|
+
* @param token - JWT token
|
|
945
|
+
*/
|
|
946
|
+
setAuthToken(token) {
|
|
947
|
+
this.client.setAuthToken(token);
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Set custom header for API requests
|
|
951
|
+
* @param key - Header key
|
|
952
|
+
* @param value - Header value
|
|
953
|
+
*/
|
|
954
|
+
setHeader(key, value) {
|
|
955
|
+
this.client.setHeader(key, value);
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Get the current auth token from storage
|
|
959
|
+
*/
|
|
960
|
+
getAuthToken() {
|
|
961
|
+
return this.auth.getToken();
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Check if user is authenticated
|
|
965
|
+
*/
|
|
966
|
+
isAuthenticated() {
|
|
967
|
+
return this.auth.isAuthenticated();
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Logout user and clear auth token
|
|
971
|
+
*/
|
|
972
|
+
logout() {
|
|
973
|
+
this.auth.logout();
|
|
974
|
+
this.client.setAuthToken(null);
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
var index_default = ResonanceSDK;
|
|
978
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
979
|
+
0 && (module.exports = {
|
|
980
|
+
AnalyticsAPI,
|
|
981
|
+
AuthService,
|
|
982
|
+
DelegatorsAPI,
|
|
983
|
+
GlobalAPI,
|
|
984
|
+
HTTPClient,
|
|
985
|
+
ReferralsAPI,
|
|
986
|
+
ResonanceSDK,
|
|
987
|
+
SlashingAPI,
|
|
988
|
+
ValidatorsAPI
|
|
989
|
+
});
|