evernode-js-client 0.5.13 → 0.5.15

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -6,13 +6,7 @@
6
6
  ],
7
7
  "homepage": "https://github.com/HotPocketDev/evernode-js-client",
8
8
  "license": "MIT",
9
- "version": "0.5.13",
10
- "scripts": {
11
- "lint": "./node_modules/.bin/eslint src/**/*.js",
12
- "build": "npm run lint && ncc build src/index.js -e elliptic -e xrpl -e ripple-address-codec -e ripple-keypairs -o dist/",
13
- "bundle": "npm run build && ./clean-pkg.sh",
14
- "publish": "npm run bundle && cp npm-readme.md dist/README.md && npm publish ./dist"
15
- },
9
+ "version": "0.5.15",
16
10
  "dependencies": {
17
11
  "elliptic": "6.5.4",
18
12
  "libsodium-wrappers": "0.7.10",
@@ -20,8 +14,5 @@
20
14
  "ripple-keypairs": "1.1.0",
21
15
  "xrpl": "2.2.1",
22
16
  "xrpl-binary-codec": "1.4.2"
23
- },
24
- "devDependencies": {
25
- "eslint": "8.3.0"
26
17
  }
27
18
  }
package/.eslintrc.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "env": {
3
- "browser": true,
4
- "commonjs": true,
5
- "es2021": true
6
- },
7
- "extends": "eslint:recommended",
8
- "parserOptions": {
9
- "ecmaVersion": 13
10
- },
11
- "rules": {
12
- "no-async-promise-executor": "off"
13
- }
14
- }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2021 HotPocketDev
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/clean-pkg.sh DELETED
@@ -1,4 +0,0 @@
1
- #!/bin/bash
2
- # Create a clean dist/package.json for publishing.
3
- node -p "const pkg=require('./package.json'); delete pkg.scripts; delete pkg.devDependencies; JSON.stringify(pkg, null, 4)" \
4
- > dist/package.json
package/npm-readme.md DELETED
@@ -1,4 +0,0 @@
1
- # Evernode library for nodejs and browser.
2
- Evernode library contains helper methods to interact with Evernode ecosystem.
3
-
4
- - [Source code](https://github.com/HotPocketDev/evernode-js-client)
@@ -1,10 +0,0 @@
1
- #!/bin/bash
2
- # This script removes previous npm package versions. (need to be logged into npm first)
3
-
4
- for i in {0..36}
5
- do
6
- pkg="evernode-js-client@0.4.$i"
7
- echo Unpublishing $pkg
8
- npm unpublish $pkg
9
- sleep 2
10
- done
@@ -1,610 +0,0 @@
1
- const codec = require('ripple-address-codec');
2
- const { Buffer } = require('buffer');
3
- const { XrplApi } = require('../xrpl-api');
4
- const { XrplAccount } = require('../xrpl-account');
5
- const { XrplApiEvents, XrplConstants } = require('../xrpl-common');
6
- const { EvernodeEvents, MemoTypes, MemoFormats, EvernodeConstants, HookStateKeys } = require('../evernode-common');
7
- const { DefaultValues } = require('../defaults');
8
- const { EncryptionHelper } = require('../encryption-helper');
9
- const { EventEmitter } = require('../event-emitter');
10
- const { UtilHelpers } = require('../util-helpers');
11
- const { FirestoreHandler } = require('../firestore/firestore-handler');
12
- const { StateHelpers } = require('../state-helpers');
13
- const { EvernodeHelpers } = require('../evernode-helpers');
14
-
15
- class BaseEvernodeClient {
16
-
17
- #watchEvents;
18
- #autoSubscribe;
19
- #ownsXrplApi = false;
20
- #firestoreHandler;
21
-
22
- constructor(xrpAddress, xrpSecret, watchEvents, autoSubscribe = false, options = {}) {
23
-
24
- this.connected = false;
25
- this.registryAddress = options.registryAddress || DefaultValues.registryAddress;
26
-
27
- this.xrplApi = options.xrplApi || DefaultValues.xrplApi || new XrplApi(options.rippledServer);
28
- if (!options.xrplApi && !DefaultValues.xrplApi)
29
- this.#ownsXrplApi = true;
30
-
31
- this.xrplAcc = new XrplAccount(xrpAddress, xrpSecret, { xrplApi: this.xrplApi });
32
- this.accKeyPair = xrpSecret && this.xrplAcc.deriveKeypair();
33
- this.#watchEvents = watchEvents;
34
- this.#autoSubscribe = autoSubscribe;
35
- this.events = new EventEmitter();
36
- this.#firestoreHandler = new FirestoreHandler()
37
-
38
- this.xrplAcc.on(XrplApiEvents.PAYMENT, (tx, error) => this.#handleEvernodeEvent(tx, error));
39
- this.xrplAcc.on(XrplApiEvents.NFT_OFFER_CREATE, (tx, error) => this.#handleEvernodeEvent(tx, error));
40
- this.xrplAcc.on(XrplApiEvents.NFT_OFFER_ACCEPT, (tx, error) => this.#handleEvernodeEvent(tx, error));
41
-
42
- }
43
-
44
- /**
45
- * Listens to the subscribed events. This will listen for the event without detaching the handler until it's 'off'.
46
- * @param {string} event Event name.
47
- * @param {function(event)} handler Callback function to handle the event.
48
- */
49
- on(event, handler) {
50
- this.events.on(event, handler);
51
- }
52
-
53
- /**
54
- * Listens to the subscribed events. This will listen only once and detach the handler.
55
- * @param {string} event Event name.
56
- * @param {function(event)} handler Callback function to handle the event.
57
- */
58
- once(event, handler) {
59
- this.events.once(event, handler);
60
- }
61
-
62
- /**
63
- * Detach the listener event.
64
- * @param {string} event Event name.
65
- * @param {function(event)} handler (optional) Can be sent if a specific handler need to be detached. All the handlers will be detached if not specified.
66
- */
67
- off(event, handler = null) {
68
- this.events.off(event, handler);
69
- }
70
-
71
- /**
72
- * Connects the client to xrpl server and do the config loading and subscriptions. 'subscribe' is called inside this.
73
- * @returns boolean value, 'true' if success.
74
- */
75
- async connect() {
76
- if (this.connected)
77
- return true;
78
-
79
- await this.xrplApi.connect();
80
-
81
- // Invoking the info command to check the account existence. This is important to
82
- // identify a network reset from XRPL.
83
- await this.xrplAcc.getInfo();
84
-
85
- this.config = await this.#getEvernodeConfig();
86
- this.connected = true;
87
-
88
- if (this.#autoSubscribe)
89
- await this.subscribe();
90
-
91
- return true;
92
- }
93
-
94
- /**
95
- * Disconnects the client to xrpl server and do the un-subscriptions. 'unsubscribe' is called inside this.
96
- */
97
- async disconnect() {
98
- await this.unsubscribe();
99
-
100
- if (this.#ownsXrplApi)
101
- await this.xrplApi.disconnect();
102
- }
103
-
104
- /**
105
- * Subscribes to the registry client events.
106
- */
107
- async subscribe() {
108
- await this.xrplAcc.subscribe();
109
- }
110
-
111
- /**
112
- * Unsubscribes from the registry client events.
113
- */
114
- async unsubscribe() {
115
- await this.xrplAcc.unsubscribe();
116
- }
117
-
118
- /**
119
- * Get the EVR balance in the registry account.
120
- * @returns The available EVR amount as a 'string'.
121
- */
122
- async getEVRBalance() {
123
- const lines = await this.xrplAcc.getTrustLines(EvernodeConstants.EVR, this.config.evrIssuerAddress);
124
- if (lines.length > 0)
125
- return lines[0].balance;
126
- else
127
- return '0';
128
- }
129
-
130
- /**
131
- * Get all XRPL hook states in the registry account.
132
- * @returns The list of hook states including Evernode configuration and hosts.
133
- */
134
- async getHookStates() {
135
- const regAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
136
- const configs = await regAcc.getNamespaceEntries(EvernodeConstants.HOOK_NAMESPACE);
137
-
138
- if (configs)
139
- return configs.filter(c => c.LedgerEntryType === 'HookState').map(c => { return { key: c.HookStateKey, data: c.HookStateData } });
140
- return [];
141
- }
142
-
143
- /**
144
- * Get the moment from the given index (timestamp).
145
- * @param {number} index [Optional] Index (timestamp) to get the moment value.
146
- * @returns The moment of the given index (timestamp) as 'number'. Returns current moment if index (timestamp) is not given.
147
- */
148
- async getMoment(index = null) {
149
- const i = index || UtilHelpers.getCurrentUnixTime();
150
- const m = this.config.momentBaseInfo.baseTransitionMoment + Math.floor((i - this.config.momentBaseInfo.baseIdx) / this.config.momentSize);
151
- await Promise.resolve();
152
- return m;
153
- }
154
-
155
- /**
156
- * Get start index (timestamp) of the moment.
157
- * @param {number} index [Optional] Index (timestamp) to get the moment value.
158
- * @returns The index (timestamp) of the moment as a 'number'. Returns the current moment's start index (timestamp) if ledger index parameter is not given.
159
- */
160
- async getMomentStartIndex(index = null) {
161
- const i = index || UtilHelpers.getCurrentUnixTime();
162
-
163
- const m = Math.floor((i - this.config.momentBaseInfo.baseIdx) / this.config.momentSize);
164
-
165
- await Promise.resolve(); // Awaiter placeholder for future async requirements.
166
- return this.config.momentBaseInfo.baseIdx + (m * this.config.momentSize);
167
- }
168
-
169
- /**
170
- * Get Evernode configuration
171
- * @returns An object with all the configuration and their values.
172
- */
173
- async #getEvernodeConfig() {
174
- let states = await this.getHookStates();
175
- const configStateKeys = {
176
- evrIssuerAddress: HookStateKeys.EVR_ISSUER_ADDR,
177
- foundationAddress: HookStateKeys.FOUNDATION_ADDR,
178
- hostRegFee: HookStateKeys.HOST_REG_FEE,
179
- momentSize: HookStateKeys.MOMENT_SIZE,
180
- hostHeartbeatFreq: HookStateKeys.HOST_HEARTBEAT_FREQ,
181
- momentBaseInfo: HookStateKeys.MOMENT_BASE_INFO,
182
- purchaserTargetPrice: HookStateKeys.PURCHASER_TARGET_PRICE,
183
- leaseAcquireWindow: HookStateKeys.LEASE_ACQUIRE_WINDOW,
184
- rewardInfo: HookStateKeys.REWARD_INFO,
185
- rewardConfiguration: HookStateKeys.REWARD_CONFIGURATION,
186
- hostCount: HookStateKeys.HOST_COUNT,
187
- momentTransitInfo: HookStateKeys.MOMENT_TRANSIT_INFO,
188
- registryMaxTrxEmitFee: HookStateKeys.MAX_TRX_EMISSION_FEE
189
- }
190
- let config = {};
191
- for (const [key, value] of Object.entries(configStateKeys)) {
192
- const stateKey = Buffer.from(value, 'hex');
193
- const stateDataBin = StateHelpers.getStateData(states, value);
194
- if (stateDataBin) {
195
- const stateData = Buffer.from(StateHelpers.getStateData(states, value), 'hex');
196
- const decoded = StateHelpers.decodeStateData(stateKey, stateData);
197
- config[key] = decoded.value;
198
- }
199
- }
200
- return config;
201
- }
202
-
203
- /**
204
- * Loads the configs from XRPL hook and updates the in-memory config.
205
- */
206
- async refreshConfig() {
207
- this.config = await this.#getEvernodeConfig();
208
- }
209
-
210
- /**
211
- * Extracts transaction info and emits the Evernode event.
212
- * @param {object} tx XRPL transaction to be handled.
213
- * @param {any} error Error if there's any.
214
- */
215
- async #handleEvernodeEvent(tx, error) {
216
- if (error)
217
- console.error(error);
218
- else if (!tx)
219
- console.log('handleEvernodeEvent: Invalid transaction.');
220
- else {
221
- const ev = await this.extractEvernodeEvent(tx);
222
- if (ev && this.#watchEvents.find(e => e === ev.name))
223
- this.events.emit(ev.name, ev.data);
224
- }
225
- }
226
-
227
- /**
228
- * Extracts the transaction info from a given transaction.
229
- * @param {object} tx Transaction to be deserialized and extracted.
230
- * @returns The event object in the format {name: '', data: {}}. Returns null if not handled. Note: You need to deserialize memos before passing the transaction to this function.
231
- */
232
- async extractEvernodeEvent(tx) {
233
- if (tx.TransactionType === 'NFTokenAcceptOffer' && tx.NFTokenSellOffer && tx.Memos.length >= 1 &&
234
- tx.Memos[0].type === MemoTypes.ACQUIRE_LEASE && tx.Memos[0].format === MemoFormats.BASE64 && tx.Memos[0].data) {
235
-
236
- // If our account is the destination host account, then decrypt the payload.
237
- let payload = tx.Memos[0].data;
238
- if (tx.Destination === this.xrplAcc.address) {
239
- const decrypted = this.accKeyPair && await EncryptionHelper.decrypt(this.accKeyPair.privateKey, payload);
240
- if (decrypted)
241
- payload = decrypted;
242
- else
243
- console.log('Failed to decrypt acquire data.');
244
- }
245
-
246
- return {
247
- name: EvernodeEvents.AcquireLease,
248
- data: {
249
- transaction: tx,
250
- host: tx.Destination,
251
- nfTokenId: tx.NFTokenSellOffer?.NFTokenID,
252
- leaseAmount: tx.NFTokenSellOffer?.Amount?.value,
253
- acquireRefId: tx.hash,
254
- tenant: tx.Account,
255
- payload: payload
256
- }
257
- }
258
- }
259
-
260
- else if (tx.TransactionType === 'NFTokenAcceptOffer' && tx.NFTokenBuyOffer && tx.Memos.length >= 1 &&
261
- tx.Memos[0].type === MemoTypes.HOST_POST_DEREG && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
262
- return {
263
- name: EvernodeEvents.HostPostDeregistered,
264
- data: {
265
- transaction: tx,
266
- nfTokenId: tx.NFTokenBuyOffer.NFTokenID,
267
- flags: tx.Flags,
268
- hash: tx.hash
269
- }
270
- }
271
- }
272
-
273
- else if (tx.Memos.length >= 2 &&
274
- tx.Memos[0].type === MemoTypes.ACQUIRE_SUCCESS && tx.Memos[0].data &&
275
- tx.Memos[1].type === MemoTypes.ACQUIRE_REF && tx.Memos[1].data) {
276
-
277
- let payload = tx.Memos[0].data;
278
- const acquireRefId = tx.Memos[1].data;
279
-
280
- // If our account is the destination user account, then decrypt the payload.
281
- if (tx.Memos[0].format === MemoFormats.BASE64 && tx.Destination === this.xrplAcc.address) {
282
- const decrypted = this.accKeyPair && await EncryptionHelper.decrypt(this.accKeyPair.privateKey, payload);
283
- if (decrypted)
284
- payload = decrypted;
285
- else
286
- console.log('Failed to decrypt instance data.');
287
- }
288
-
289
- return {
290
- name: EvernodeEvents.AcquireSuccess,
291
- data: {
292
- transaction: tx,
293
- acquireRefId: acquireRefId,
294
- payload: payload
295
- }
296
- }
297
-
298
- }
299
- else if (tx.Memos.length >= 2 &&
300
- tx.Memos[0].type === MemoTypes.ACQUIRE_ERROR && tx.Memos[0].data &&
301
- tx.Memos[1].type === MemoTypes.ACQUIRE_REF && tx.Memos[1].data) {
302
-
303
- let error = tx.Memos[0].data;
304
- const acquireRefId = tx.Memos[1].data;
305
-
306
- if (tx.Memos[0].format === MemoFormats.JSON)
307
- error = JSON.parse(error).reason;
308
-
309
- return {
310
- name: EvernodeEvents.AcquireError,
311
- data: {
312
- transaction: tx,
313
- acquireRefId: acquireRefId,
314
- reason: error
315
- }
316
- }
317
- }
318
- else if (tx.Memos.length >= 1 &&
319
- tx.Memos[0].type === MemoTypes.HOST_REG && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
320
-
321
- return {
322
- name: EvernodeEvents.HostRegistered,
323
- data: {
324
- transaction: tx,
325
- host: tx.Account
326
- }
327
- }
328
- }
329
- else if (tx.Memos.length >= 1 && tx.Memos[0].type === MemoTypes.HOST_DEREG) {
330
- return {
331
- name: EvernodeEvents.HostDeregistered,
332
- data: {
333
- transaction: tx,
334
- host: tx.Account
335
- }
336
- }
337
- }
338
- else if (tx.Memos.length >= 1 &&
339
- tx.Memos[0].type === MemoTypes.HEARTBEAT) {
340
-
341
- return {
342
- name: EvernodeEvents.Heartbeat,
343
- data: {
344
- transaction: tx,
345
- host: tx.Account
346
- }
347
- }
348
- }
349
- else if (tx.Memos.length >= 1 &&
350
- tx.Memos[0].type === MemoTypes.EXTEND_LEASE && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
351
-
352
- let nfTokenId = tx.Memos[0].data;
353
-
354
- return {
355
- name: EvernodeEvents.ExtendLease,
356
- data: {
357
- transaction: tx,
358
- extendRefId: tx.hash,
359
- tenant: tx.Account,
360
- currency: tx.Amount.currency,
361
- payment: parseFloat(tx.Amount.value),
362
- nfTokenId: nfTokenId
363
- }
364
- }
365
- }
366
- else if (tx.Memos.length >= 2 &&
367
- tx.Memos[0].type === MemoTypes.EXTEND_SUCCESS && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data &&
368
- tx.Memos[1].type === MemoTypes.EXTEND_REF && tx.Memos[1].format === MemoFormats.HEX && tx.Memos[1].data) {
369
-
370
- const extendResBuf = Buffer.from(tx.Memos[0].data, 'hex');
371
- const extendRefId = tx.Memos[1].data;
372
-
373
- return {
374
- name: EvernodeEvents.ExtendSuccess,
375
- data: {
376
- transaction: tx,
377
- extendRefId: extendRefId,
378
- expiryMoment: extendResBuf.readUInt32BE()
379
- }
380
- }
381
-
382
- }
383
- else if (tx.Memos.length >= 2 &&
384
- tx.Memos[0].type === MemoTypes.EXTEND_ERROR && tx.Memos[0].data &&
385
- tx.Memos[1].type === MemoTypes.EXTEND_REF && tx.Memos[1].data) {
386
-
387
- let error = tx.Memos[0].data;
388
- const extendRefId = tx.Memos[1].data;
389
-
390
- if (tx.Memos[0].format === MemoFormats.JSON)
391
- error = JSON.parse(error).reason;
392
-
393
- return {
394
- name: EvernodeEvents.ExtendError,
395
- data: {
396
- transaction: tx,
397
- extendRefId: extendRefId,
398
- reason: error
399
- }
400
- }
401
- }
402
- else if (tx.Memos.length >= 1 &&
403
- tx.Memos[0].type === MemoTypes.REGISTRY_INIT && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
404
-
405
- return {
406
- name: EvernodeEvents.RegistryInitialized,
407
- data: {
408
- transaction: tx
409
- }
410
- }
411
- }
412
- else if (tx.Memos.length >= 1 &&
413
- tx.Memos[0].type === MemoTypes.HOST_UPDATE_INFO && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
414
-
415
- return {
416
- name: EvernodeEvents.HostRegUpdated,
417
- data: {
418
- transaction: tx,
419
- host: tx.Account
420
- }
421
- }
422
- }
423
- else if (tx.Memos.length >= 1 &&
424
- tx.Memos[0].type === MemoTypes.DEAD_HOST_PRUNE && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
425
-
426
- const addrsBuf = Buffer.from(tx.Memos[0].data, 'hex');
427
-
428
- return {
429
- name: EvernodeEvents.DeadHostPrune,
430
- data: {
431
- transaction: tx,
432
- host: codec.encodeAccountID(addrsBuf)
433
- }
434
- }
435
- }
436
- else if (tx.Memos.length >= 1 &&
437
- tx.Memos[0].type === MemoTypes.HOST_REBATE) {
438
-
439
- return {
440
- name: EvernodeEvents.HostRebate,
441
- data: {
442
- transaction: tx,
443
- host: tx.Account
444
- }
445
- }
446
- }
447
- else if (tx.Memos.length >= 1 &&
448
- tx.Memos[0].type === MemoTypes.HOST_TRANSFER && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
449
-
450
- const addrsBuf = Buffer.from(tx.Memos[0].data, 'hex');
451
-
452
- return {
453
- name: EvernodeEvents.HostTransfer,
454
- data: {
455
- transaction: tx,
456
- transferee: codec.encodeAccountID(addrsBuf)
457
- }
458
- }
459
- }
460
-
461
- return null;
462
- }
463
-
464
- /**
465
- * Get the registered host information.
466
- * @param {string} hostAddress [Optional] Address of the host.
467
- * @returns The registered host information object. Returns null is not registered.
468
- */
469
- async getHostInfo(hostAddress = this.xrplAcc.address) {
470
- try {
471
- const addrStateKey = StateHelpers.generateHostAddrStateKey(hostAddress);
472
- const addrStateIndex = StateHelpers.getHookStateIndex(this.registryAddress, addrStateKey);
473
- const addrLedgerEntry = await this.xrplApi.getLedgerEntry(addrStateIndex);
474
- const addrStateData = addrLedgerEntry?.HookStateData;
475
- if (addrStateData) {
476
- const addrStateDecoded = StateHelpers.decodeHostAddressState(Buffer.from(addrStateKey, 'hex'), Buffer.from(addrStateData, 'hex'));
477
- const curMomentStartIdx = await this.getMomentStartIndex();
478
- addrStateDecoded.active = (addrStateDecoded.lastHeartbeatIndex > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
479
- (addrStateDecoded.lastHeartbeatIndex >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
480
- (addrStateDecoded.lastHeartbeatIndex > 0))
481
-
482
- const nftIdStatekey = StateHelpers.generateTokenIdStateKey(addrStateDecoded.nfTokenId);
483
- const nftIdStateIndex = StateHelpers.getHookStateIndex(this.registryAddress, nftIdStatekey);
484
- const nftIdLedgerEntry = await this.xrplApi.getLedgerEntry(nftIdStateIndex);
485
-
486
- const nftIdStateData = nftIdLedgerEntry?.HookStateData;
487
- if (nftIdStateData) {
488
- const nftIdStateDecoded = StateHelpers.decodeTokenIdState(Buffer.from(nftIdStateData, 'hex'));
489
- return { ...addrStateDecoded, ...nftIdStateDecoded };
490
- }
491
- }
492
- }
493
- catch (e) {
494
- // If the exception is entryNotFound from Rippled there's no entry for the host, So return null.
495
- if (e?.data?.error !== 'entryNotFound')
496
- throw e;
497
- }
498
-
499
- return null;
500
- }
501
-
502
- /**
503
- * Get all the hosts registered in Evernode. The result's are paginated. Default page size is 20. Note: Specifying both filter and pagination does not supported.
504
- * @param {object} filters [Optional] Filter criteria to filter the hosts. The filter key can be a either property of the host.
505
- * @param {number} pageSize [Optional] Page size for the results.
506
- * @param {string} nextPageToken [Optional] Next page's token, If received by the previous result set.
507
- * @returns The list of active hosts. The response will be in '{data: [], nextPageToken: ''}' only if there are more pages. Otherwise the response will only contain the host list.
508
- */
509
- async getHosts(filters = null, pageSize = null, nextPageToken = null) {
510
- const hosts = await this.#firestoreHandler.getHosts(filters, pageSize, nextPageToken);
511
- const curMomentStartIdx = await this.getMomentStartIndex();
512
- const res = await Promise.all((hosts.nextPageToken ? hosts.data : hosts).map(async host => {
513
- const hostAcc = new XrplAccount(host.address);
514
- host.domain = await hostAcc.getDomain();
515
-
516
- host.active = (host.lastHeartbeatIndex > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
517
- (host.lastHeartbeatIndex >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
518
- (host.lastHeartbeatIndex > 0));
519
- return host;
520
- }));
521
-
522
- return (hosts.nextPageToken ? {...hosts, data: res} : res);
523
- }
524
-
525
- /**
526
- * Get all Evernode configuration without paginating.
527
- * @returns The list of configuration.
528
- */
529
- async getAllConfigs() {
530
- let fullConfigList = [];
531
- const configs = await this.#firestoreHandler.getConfigs();
532
- if (configs.nextPageToken) {
533
- let currentPageToken = configs.nextPageToken;
534
- let nextConfigs = null;
535
- fullConfigList = fullConfigList.concat(configs.data);
536
- while (currentPageToken) {
537
- nextConfigs = await this.#firestoreHandler.getConfigs(null, 50, currentPageToken);
538
- fullConfigList = fullConfigList.concat(nextConfigs.nextPageToken ? nextConfigs.data : nextConfigs);
539
- currentPageToken = nextConfigs.nextPageToken;
540
- }
541
- } else {
542
- fullConfigList = fullConfigList.concat(configs);
543
- }
544
-
545
- return fullConfigList;
546
- }
547
-
548
- /**
549
- * Get all the hosts without paginating.
550
- * @returns The list of hosts.
551
- */
552
- async getAllHosts() {
553
- let fullHostList = [];
554
- const hosts = await this.#firestoreHandler.getHosts();
555
- if (hosts.nextPageToken) {
556
- let currentPageToken = hosts.nextPageToken;
557
- let nextHosts = null;
558
- fullHostList = fullHostList.concat(hosts.data);
559
- while (currentPageToken) {
560
- nextHosts = await this.#firestoreHandler.getHosts(null, 50, currentPageToken);
561
- fullHostList = fullHostList.concat(nextHosts.nextPageToken ? nextHosts.data : nextHosts);
562
- currentPageToken = nextHosts.nextPageToken;
563
- }
564
- } else {
565
- fullHostList = fullHostList.concat(hosts);
566
- }
567
-
568
- return fullHostList;
569
- }
570
-
571
- /**
572
- * Remove a host which is inactive for a long period. The inactivity is checked by Evernode it self and only pruned if inactive thresholds are met.
573
- * @param {string} hostAddress XRPL address of the host to be pruned.
574
- */
575
- async pruneDeadHost(hostAddress) {
576
- if (this.xrplAcc.address === this.registryAddress)
577
- throw 'Invalid function call';
578
-
579
- let memoData = Buffer.allocUnsafe(20);
580
- codec.decodeAccountID(hostAddress).copy(memoData);
581
-
582
- // To obtain registration NFT Page Keylet and index.
583
- const hostAcc = new XrplAccount(hostAddress, null, { xrplApi: this.xrplApi });
584
- const regNFT = (await hostAcc.getNfts()).find(n => n.URI.startsWith(EvernodeConstants.NFT_PREFIX_HEX) && n.Issuer === this.registryAddress);
585
- if (regNFT) {
586
- // Check whether the token was actually issued from Evernode registry contract.
587
- const issuerHex = regNFT.NFTokenID.substr(8, 40);
588
- const issuerAddr = codec.encodeAccountID(Buffer.from(issuerHex, 'hex'));
589
- if (issuerAddr == this.registryAddress) {
590
- const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, hostAcc, this.xrplApi);
591
-
592
- await this.xrplAcc.makePayment(this.registryAddress,
593
- XrplConstants.MIN_XRP_AMOUNT,
594
- XrplConstants.XRP,
595
- null,
596
- [
597
- { type: MemoTypes.DEAD_HOST_PRUNE, format: MemoFormats.HEX, data: memoData.toString('hex') },
598
- { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
599
- ]);
600
- } else
601
- throw "Invalid Registration NFT."
602
- } else
603
- throw "No Registration NFT was found for the Host account."
604
-
605
- }
606
- }
607
-
608
- module.exports = {
609
- BaseEvernodeClient
610
- }