evernode-js-client 0.5.11 → 0.5.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,560 +0,0 @@
1
- const { XrplConstants } = require('../xrpl-common');
2
- const { BaseEvernodeClient } = require('./base-evernode-client');
3
- const { EvernodeEvents, EvernodeConstants, MemoFormats, MemoTypes, ErrorCodes } = require('../evernode-common');
4
- const { XrplAccount } = require('../xrpl-account');
5
- const { EncryptionHelper } = require('../encryption-helper');
6
- const { Buffer } = require('buffer');
7
- const codec = require('ripple-address-codec');
8
- const { XflHelpers } = require('../xfl-helpers');
9
- const { EvernodeHelpers } = require('../evernode-helpers');
10
- const { StateHelpers } = require('../state-helpers');
11
-
12
- const OFFER_WAIT_TIMEOUT = 60;
13
-
14
- const HostEvents = {
15
- AcquireLease: EvernodeEvents.AcquireLease,
16
- ExtendLease: EvernodeEvents.ExtendLease
17
- }
18
-
19
- const HOST_COUNTRY_CODE_MEMO_OFFSET = 0;
20
- const HOST_CPU_MICROSEC_MEMO_OFFSET = 2;
21
- const HOST_RAM_MB_MEMO_OFFSET = 6;
22
- const HOST_DISK_MB_MEMO_OFFSET = 10;
23
- const HOST_TOT_INS_COUNT_MEMO_OFFSET = 14;
24
- const HOST_CPU_MODEL_NAME_MEMO_OFFSET = 18;
25
- const HOST_CPU_COUNT_MEMO_OFFSET = 58;
26
- const HOST_CPU_SPEED_MEMO_OFFSET = 60;
27
- const HOST_DESCRIPTION_MEMO_OFFSET = 62;
28
- const HOST_EMAIL_ADDRESS_MEMO_OFFSET = 88;
29
- const HOST_REG_MEMO_SIZE = 128;
30
-
31
- const HOST_UPDATE_TOKEN_ID_MEMO_OFFSET = 0;
32
- const HOST_UPDATE_COUNTRY_CODE_MEMO_OFFSET = 32;
33
- const HOST_UPDATE_CPU_MICROSEC_MEMO_OFFSET = 34;
34
- const HOST_UPDATE_RAM_MB_MEMO_OFFSET = 38;
35
- const HOST_UPDATE_DISK_MB_MEMO_OFFSET = 42;
36
- const HOST_UPDATE_TOT_INS_COUNT_MEMO_OFFSET = 46;
37
- const HOST_UPDATE_ACT_INS_COUNT_MEMO_OFFSET = 50;
38
- const HOST_UPDATE_DESCRIPTION_MEMO_OFFSET = 54;
39
- const HOST_UPDATE_VERSION_MEMO_OFFSET = 80;
40
- const HOST_UPDATE_MEMO_SIZE = 83;
41
-
42
- class HostClient extends BaseEvernodeClient {
43
-
44
- constructor(xrpAddress, xrpSecret, options = {}) {
45
- super(xrpAddress, xrpSecret, Object.values(HostEvents), true, options);
46
- }
47
-
48
- async getRegistrationNft() {
49
- // Find an owned NFT with matching Evernode host NFT prefix.
50
- const nft = (await this.xrplAcc.getNfts()).find(n => n.URI.startsWith(EvernodeConstants.NFT_PREFIX_HEX) && n.Issuer === this.registryAddress);
51
- if (nft) {
52
- // Check whether the token was actually issued from Evernode registry contract.
53
- const issuerHex = nft.NFTokenID.substr(8, 40);
54
- const issuerAddr = codec.encodeAccountID(Buffer.from(issuerHex, 'hex'));
55
- if (issuerAddr == this.registryAddress) {
56
- return nft;
57
- }
58
- }
59
-
60
- return null;
61
- }
62
-
63
- async getRegistration() {
64
- // Check whether we own an evernode host token.
65
- const nft = await this.getRegistrationNft();
66
- if (nft) {
67
- const host = await this.getHostInfo();
68
- return (host?.nfTokenId == nft.NFTokenID) ? host : null;
69
- }
70
-
71
- return null;
72
- }
73
-
74
- async getLeaseOffers() {
75
- return await EvernodeHelpers.getLeaseOffers(this.xrplAcc);
76
- }
77
-
78
- async cancelOffer(offerIndex) {
79
- return this.xrplAcc.cancelOffer(offerIndex);
80
- }
81
-
82
- async isRegistered() {
83
- return (await this.getRegistration()) !== null;
84
- }
85
-
86
- async prepareAccount(domain) {
87
- const [flags, trustLines, msgKey, curDomain] = await Promise.all([
88
- this.xrplAcc.getFlags(),
89
- this.xrplAcc.getTrustLines(EvernodeConstants.EVR, this.config.evrIssuerAddress),
90
- this.xrplAcc.getMessageKey(),
91
- this.xrplAcc.getDomain()]);
92
-
93
- let accountSetFields = {};
94
- accountSetFields = (!flags.lsfDefaultRipple) ? { ...accountSetFields, Flags: { asfDefaultRipple: true } } : accountSetFields;
95
- accountSetFields = (!msgKey) ? { ...accountSetFields, MessageKey: this.accKeyPair.publicKey } : accountSetFields;
96
-
97
- domain = domain.toLowerCase();
98
- accountSetFields = (!curDomain || curDomain !== domain) ?
99
- { ...accountSetFields, Domain: domain } : accountSetFields;
100
-
101
- if (Object.keys(accountSetFields).length !== 0)
102
- await this.xrplAcc.setAccountFields(accountSetFields);
103
-
104
- if (trustLines.length === 0)
105
- await this.xrplAcc.setTrustLine(EvernodeConstants.EVR, this.config.evrIssuerAddress, "99999999999999");
106
- }
107
-
108
- async offerLease(leaseIndex, leaseAmount, tosHash) {
109
- // <prefix><lease index 16)><half of tos hash><lease amount (uint32)>
110
- const prefixLen = EvernodeConstants.LEASE_NFT_PREFIX_HEX.length / 2;
111
- const halfToSLen = tosHash.length / 4;
112
- const uriBuf = Buffer.allocUnsafe(prefixLen + halfToSLen + 10);
113
- Buffer.from(EvernodeConstants.LEASE_NFT_PREFIX_HEX, 'hex').copy(uriBuf);
114
- uriBuf.writeUInt16BE(leaseIndex, prefixLen);
115
- Buffer.from(tosHash, 'hex').copy(uriBuf, prefixLen + 2, 0, halfToSLen);
116
- uriBuf.writeBigInt64BE(XflHelpers.getXfl(leaseAmount.toString()), prefixLen + 2 + halfToSLen);
117
- const uri = uriBuf.toString('hex').toUpperCase();
118
-
119
- await this.xrplAcc.mintNft(uri, 0, 0, { isBurnable: true, isHexUri: true });
120
-
121
- const nft = await this.xrplAcc.getNftByUri(uri, true);
122
- if (!nft)
123
- throw "Offer lease NFT creation error.";
124
-
125
- await this.xrplAcc.offerSellNft(nft.NFTokenID,
126
- leaseAmount.toString(),
127
- EvernodeConstants.EVR,
128
- this.config.evrIssuerAddress);
129
- }
130
-
131
- async expireLease(nfTokenId, tenantAddress = null) {
132
- await this.xrplAcc.burnNft(nfTokenId, tenantAddress);
133
- }
134
-
135
- async register(countryCode, cpuMicroSec, ramMb, diskMb, totalInstanceCount, cpuModel, cpuCount, cpuSpeed, description, emailAddress, options = {}) {
136
- if (!/^([A-Z]{2})$/.test(countryCode))
137
- throw "countryCode should consist of 2 uppercase alphabetical characters";
138
- else if (!cpuMicroSec || isNaN(cpuMicroSec) || cpuMicroSec % 1 != 0 || cpuMicroSec < 0)
139
- throw "cpuMicroSec should be a positive integer";
140
- else if (!ramMb || isNaN(ramMb) || ramMb % 1 != 0 || ramMb < 0)
141
- throw "ramMb should be a positive integer";
142
- else if (!diskMb || isNaN(diskMb) || diskMb % 1 != 0 || diskMb < 0)
143
- throw "diskMb should be a positive integer";
144
- else if (!totalInstanceCount || isNaN(totalInstanceCount) || totalInstanceCount % 1 != 0 || totalInstanceCount < 0)
145
- throw "totalInstanceCount should be a positive intiger";
146
- else if (!cpuCount || isNaN(cpuCount) || cpuCount % 1 != 0 || cpuCount < 0)
147
- throw "CPU count should be a positive integer";
148
- else if (!cpuSpeed || isNaN(cpuSpeed) || cpuSpeed % 1 != 0 || cpuSpeed < 0)
149
- throw "CPU speed should be a positive integer";
150
- else if (!cpuModel)
151
- throw "cpu model cannot be empty";
152
-
153
- // Need to use control characters inside this regex to match ascii characters.
154
- // Here we allow all the characters in ascii range except ";" for the description.
155
- // no-control-regex is enabled default by eslint:recommended, So we disable it only for next line.
156
- // eslint-disable-next-line no-control-regex
157
- else if (!/^((?![;])[\x00-\x7F]){0,26}$/.test(description))
158
- throw "description should consist of 0-26 ascii characters except ';'";
159
-
160
- else if (!emailAddress || !(/[a-z0-9]+@[a-z]+.[a-z]{2,3}/.test(emailAddress)) || (emailAddress.length > 40))
161
- throw "Email address should be valid and can not have more than 40 characters.";
162
-
163
- if (await this.isRegistered())
164
- throw "Host already registered.";
165
-
166
- // Check whether is there any missed NFT sell offer that needs to be accepted
167
- // from the client-side in order to complete the registration.
168
- const regNft = await this.getRegistrationNft();
169
- if (!regNft) {
170
- const regInfo = await this.getHostInfo(this.xrplAcc.address);
171
- if (regInfo) {
172
- const registryAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
173
- const sellOffer = (await registryAcc.getNftOffers()).find(o => o.NFTokenID == regInfo.nfTokenId);
174
- if (sellOffer) {
175
- await this.xrplAcc.buyNft(sellOffer.index);
176
- console.log("Registration was successfully completed after acquiring the NFT.");
177
- return await this.isRegistered();
178
- }
179
- }
180
- }
181
-
182
- // Check the availability of an initiated transfer.
183
- // Need to modify the amount accordingly.
184
- const stateTransfereeAddrKey = StateHelpers.generateTransfereeAddrStateKey(this.xrplAcc.address);
185
- const stateTransfereeAddrIndex = StateHelpers.getHookStateIndex(this.registryAddress, stateTransfereeAddrKey);
186
- let transfereeAddrLedgerEntry = {};
187
- let transfereeAddrStateData = {};
188
- let transferredNFTokenId = null;
189
-
190
- try {
191
- const res = await this.xrplApi.getLedgerEntry(stateTransfereeAddrIndex);
192
- transfereeAddrLedgerEntry = { ...transfereeAddrLedgerEntry, ...res };
193
- transfereeAddrStateData = transfereeAddrLedgerEntry?.HookStateData;
194
- const transfereeAddrStateDecoded = StateHelpers.decodeTransfereeAddrState(Buffer.from(stateTransfereeAddrKey, 'hex'), Buffer.from(transfereeAddrStateData, 'hex'));
195
- transferredNFTokenId = transfereeAddrStateDecoded?.transferredNfTokenId;
196
-
197
- }
198
- catch (e) {
199
- console.log("No initiated transfers were found.");
200
- }
201
-
202
- // <country_code(2)><cpu_microsec(4)><ram_mb(4)><disk_mb(4)><no_of_total_instances(4)><cpu_model(40)><cpu_count(2)><cpu_speed(2)><description(26)><email_address(40)>
203
- const memoBuf = Buffer.alloc(HOST_REG_MEMO_SIZE, 0);
204
- Buffer.from(countryCode.substr(0, 2), "utf-8").copy(memoBuf, HOST_COUNTRY_CODE_MEMO_OFFSET);
205
- memoBuf.writeUInt32BE(cpuMicroSec, HOST_CPU_MICROSEC_MEMO_OFFSET);
206
- memoBuf.writeUInt32BE(ramMb, HOST_RAM_MB_MEMO_OFFSET);
207
- memoBuf.writeUInt32BE(diskMb, HOST_DISK_MB_MEMO_OFFSET);
208
- memoBuf.writeUInt32BE(totalInstanceCount, HOST_TOT_INS_COUNT_MEMO_OFFSET);
209
- Buffer.from(cpuModel.substr(0, 40), "utf-8").copy(memoBuf, HOST_CPU_MODEL_NAME_MEMO_OFFSET);
210
- memoBuf.writeUInt16BE(cpuCount, HOST_CPU_COUNT_MEMO_OFFSET);
211
- memoBuf.writeUInt16BE(cpuSpeed, HOST_CPU_SPEED_MEMO_OFFSET);
212
- Buffer.from(description.substr(0, 26), "utf-8").copy(memoBuf, HOST_DESCRIPTION_MEMO_OFFSET);
213
- Buffer.from(emailAddress.substr(0, 40), "utf-8").copy(memoBuf, HOST_EMAIL_ADDRESS_MEMO_OFFSET);
214
-
215
- const tx = await this.xrplAcc.makePayment(this.registryAddress,
216
- (transferredNFTokenId) ? EvernodeConstants.NOW_IN_EVRS : this.config.hostRegFee.toString(),
217
- EvernodeConstants.EVR,
218
- this.config.evrIssuerAddress,
219
- [{ type: MemoTypes.HOST_REG, format: MemoFormats.HEX, data: memoBuf.toString('hex') }],
220
- options.transactionOptions);
221
-
222
- console.log('Waiting for the sell offer')
223
- const regAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
224
- let offer = null;
225
- let attempts = 0;
226
- let offerLedgerIndex = 0;
227
- while (attempts < OFFER_WAIT_TIMEOUT) {
228
- const nft = (await regAcc.getNfts()).find(n => (n.URI === `${EvernodeConstants.NFT_PREFIX_HEX}${tx.id}`) || (n.NFTokenID === transferredNFTokenId));
229
- if (nft) {
230
- offer = (await regAcc.getNftOffers()).find(o => o.Destination === this.xrplAcc.address && o.NFTokenID === nft.NFTokenID && o.Flags === 1);
231
- offerLedgerIndex = this.xrplApi.ledgerIndex;
232
- if (offer)
233
- break;
234
- await new Promise(resolve => setTimeout(resolve, 1000));
235
- attempts++;
236
- }
237
-
238
- }
239
- if (!offer)
240
- throw 'No sell offer found within timeout.';
241
-
242
- console.log('Accepting the sell offer..');
243
-
244
- // Wait until the next ledger after the offer is created.
245
- // Otherwise if the offer accepted in the same legder which it's been created,
246
- // We cannot fetch the offer from registry contract event handler since it's getting deleted immediately.
247
- await new Promise(async resolve => {
248
- while (this.xrplApi.ledgerIndex <= offerLedgerIndex)
249
- await new Promise(resolve2 => setTimeout(resolve2, 1000));
250
- resolve();
251
- });
252
-
253
- await this.xrplAcc.buyNft(offer.index);
254
-
255
- return await this.isRegistered();
256
- }
257
-
258
- async deregister(options = {}) {
259
-
260
- if (!(await this.isRegistered()))
261
- throw "Host not registered."
262
-
263
- const regNFT = await this.getRegistrationNft();
264
-
265
- // To obtain registration NFT Page Keylet and index.
266
- const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, this.xrplAcc, this.xrplApi);
267
-
268
- await this.xrplAcc.makePayment(this.registryAddress,
269
- XrplConstants.MIN_XRP_AMOUNT,
270
- XrplConstants.XRP,
271
- null,
272
- [
273
- { type: MemoTypes.HOST_DEREG, format: MemoFormats.HEX, data: regNFT.NFTokenID },
274
- { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
275
- ],
276
- options.transactionOptions);
277
-
278
- console.log('Waiting for the buy offer')
279
- const regAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
280
- let offer = null;
281
- let attempts = 0;
282
- let offerLedgerIndex = 0;
283
- while (attempts < OFFER_WAIT_TIMEOUT) {
284
- offer = (await regAcc.getNftOffers()).find(o => (o.NFTokenID == regNFT.NFTokenID) && (o.Flags === 0));
285
- offerLedgerIndex = this.xrplApi.ledgerIndex;
286
- if (offer)
287
- break;
288
- await new Promise(resolve => setTimeout(resolve, 1000));
289
- attempts++;
290
- }
291
- if (!offer)
292
- throw 'No buy offer found within timeout.';
293
-
294
- console.log('Accepting the buy offer..');
295
-
296
- // Wait until the next ledger after the offer is created.
297
- // Otherwise if the offer accepted in the same legder which it's been created,
298
- // We cannot fetch the offer from registry contract event handler since it's getting deleted immediately.
299
- await new Promise(async resolve => {
300
- while (this.xrplApi.ledgerIndex <= offerLedgerIndex)
301
- await new Promise(resolve2 => setTimeout(resolve2, 1000));
302
- resolve();
303
- });
304
-
305
- await this.xrplAcc.sellNft(
306
- offer.index,
307
- [{ type: MemoTypes.HOST_POST_DEREG, format: MemoFormats.HEX, data: regNFT.NFTokenID }]
308
- );
309
-
310
- return await this.isRegistered();
311
- }
312
-
313
- async updateRegInfo(activeInstanceCount = null, version = null, totalInstanceCount = null, tokenID = null, countryCode = null, cpuMicroSec = null, ramMb = null, diskMb = null, description = null, options = {}) {
314
- // <token_id(32)><country_code(2)><cpu_microsec(4)><ram_mb(4)><disk_mb(4)><total_instance_count(4)><active_instances(4)><description(26)><version(3)>
315
- const memoBuf = Buffer.alloc(HOST_UPDATE_MEMO_SIZE, 0);
316
- if (tokenID)
317
- Buffer.from(tokenID.substr(0, 32), "hex").copy(memoBuf, HOST_UPDATE_TOKEN_ID_MEMO_OFFSET);
318
- if (countryCode)
319
- Buffer.from(countryCode.substr(0, 2), "utf-8").copy(memoBuf, HOST_UPDATE_COUNTRY_CODE_MEMO_OFFSET);
320
- if (cpuMicroSec)
321
- memoBuf.writeUInt32BE(cpuMicroSec, HOST_UPDATE_CPU_MICROSEC_MEMO_OFFSET);
322
- if (ramMb)
323
- memoBuf.writeUInt32BE(ramMb, HOST_UPDATE_RAM_MB_MEMO_OFFSET);
324
- if (diskMb)
325
- memoBuf.writeUInt32BE(diskMb, HOST_UPDATE_DISK_MB_MEMO_OFFSET);
326
- if (totalInstanceCount)
327
- memoBuf.writeUInt32BE(totalInstanceCount, HOST_UPDATE_TOT_INS_COUNT_MEMO_OFFSET);
328
- if (activeInstanceCount)
329
- memoBuf.writeUInt32BE(activeInstanceCount, HOST_UPDATE_ACT_INS_COUNT_MEMO_OFFSET);
330
- if (description)
331
- Buffer.from(description.substr(0, 26), "utf-8").copy(memoBuf, HOST_UPDATE_DESCRIPTION_MEMO_OFFSET);
332
- if (version) {
333
- const components = version.split('.').map(v => parseInt(v));
334
- if (components.length != 3)
335
- throw 'Invalid version format.';
336
- memoBuf.writeUInt8(components[0], HOST_UPDATE_VERSION_MEMO_OFFSET);
337
- memoBuf.writeUInt8(components[1], HOST_UPDATE_VERSION_MEMO_OFFSET + 1);
338
- memoBuf.writeUInt8(components[2], HOST_UPDATE_VERSION_MEMO_OFFSET + 2);
339
- }
340
-
341
- // To obtain registration NFT Page Keylet and index.
342
- if (!tokenID)
343
- tokenID = (await this.getRegistrationNft()).NFTokenID;
344
- const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(tokenID, this.xrplAcc, this.xrplApi);
345
-
346
- return await this.xrplAcc.makePayment(this.registryAddress,
347
- XrplConstants.MIN_XRP_AMOUNT,
348
- XrplConstants.XRP,
349
- null,
350
- [
351
- { type: MemoTypes.HOST_UPDATE_INFO, format: MemoFormats.HEX, data: memoBuf.toString('hex') },
352
- { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
353
- ],
354
- options.transactionOptions);
355
- }
356
-
357
- async heartbeat(options = {}) {
358
- // To obtain registration NFT Page Keylet and index.
359
- const regNFT = await this.getRegistrationNft();
360
- const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, this.xrplAcc, this.xrplApi);
361
-
362
- return this.xrplAcc.makePayment(this.registryAddress,
363
- XrplConstants.MIN_XRP_AMOUNT,
364
- XrplConstants.XRP,
365
- null,
366
- [
367
- { type: MemoTypes.HEARTBEAT, format: "", data: "" },
368
- { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
369
- ],
370
- options.transactionOptions);
371
- }
372
-
373
- async acquireSuccess(txHash, tenantAddress, instanceInfo, options = {}) {
374
-
375
- // Encrypt the instance info with the tenant's encryption key (Specified in MessageKey field of the tenant account).
376
- const tenantAcc = new XrplAccount(tenantAddress, null, { xrplApi: this.xrplApi });
377
- const encKey = await tenantAcc.getMessageKey();
378
- if (!encKey)
379
- throw "Tenant encryption key not set.";
380
-
381
- const encrypted = await EncryptionHelper.encrypt(encKey, instanceInfo);
382
- const memos = [
383
- { type: MemoTypes.ACQUIRE_SUCCESS, format: MemoFormats.BASE64, data: encrypted },
384
- { type: MemoTypes.ACQUIRE_REF, format: MemoFormats.HEX, data: txHash }];
385
-
386
- return this.xrplAcc.makePayment(tenantAddress,
387
- XrplConstants.MIN_XRP_AMOUNT,
388
- XrplConstants.XRP,
389
- null,
390
- memos,
391
- options.transactionOptions);
392
- }
393
-
394
- async acquireError(txHash, tenantAddress, leaseAmount, reason, options = {}) {
395
-
396
- const memos = [
397
- { type: MemoTypes.ACQUIRE_ERROR, format: MemoFormats.JSON, data: { type: ErrorCodes.ACQUIRE_ERR, reason: reason } },
398
- { type: MemoTypes.ACQUIRE_REF, format: MemoFormats.HEX, data: txHash }];
399
-
400
- return this.xrplAcc.makePayment(tenantAddress,
401
- leaseAmount.toString(),
402
- EvernodeConstants.EVR,
403
- this.config.evrIssuerAddress,
404
- memos,
405
- options.transactionOptions);
406
- }
407
-
408
- async extendSuccess(txHash, tenantAddress, expiryMoment, options = {}) {
409
- let buf = Buffer.allocUnsafe(4);
410
- buf.writeUInt32BE(expiryMoment);
411
-
412
- const memos = [
413
- { type: MemoTypes.EXTEND_SUCCESS, format: MemoFormats.HEX, data: buf.toString('hex') },
414
- { type: MemoTypes.EXTEND_REF, format: MemoFormats.HEX, data: txHash }];
415
-
416
- return this.xrplAcc.makePayment(tenantAddress,
417
- XrplConstants.MIN_XRP_AMOUNT,
418
- XrplConstants.XRP,
419
- null,
420
- memos,
421
- options.transactionOptions);
422
- }
423
-
424
- async extendError(txHash, tenantAddress, reason, refund, options = {}) {
425
-
426
- const memos = [
427
- { type: MemoTypes.EXTEND_ERROR, format: MemoFormats.JSON, data: { type: ErrorCodes.EXTEND_ERR, reason: reason } },
428
- { type: MemoTypes.EXTEND_REF, format: MemoFormats.HEX, data: txHash }];
429
-
430
- // Required to refund the paid EVR amount as the offer extention is not successfull.
431
- return this.xrplAcc.makePayment(tenantAddress,
432
- refund.toString(),
433
- EvernodeConstants.EVR,
434
- this.config.evrIssuerAddress,
435
- memos,
436
- options.transactionOptions);
437
- }
438
-
439
- async refundTenant(txHash, tenantAddress, refundAmount, options = {}) {
440
- const memos = [
441
- { type: MemoTypes.REFUND, format: '', data: '' },
442
- { type: MemoTypes.REFUND_REF, format: MemoFormats.HEX, data: txHash }];
443
-
444
- return this.xrplAcc.makePayment(tenantAddress,
445
- refundAmount.toString(),
446
- EvernodeConstants.EVR,
447
- this.config.evrIssuerAddress,
448
- memos,
449
- options.transactionOptions);
450
- }
451
-
452
- async requestRebate(options = {}) {
453
-
454
- // To obtain registration NFT Page Keylet and index.
455
- const regNFT = await this.getRegistrationNft();
456
- const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, this.xrplAcc, this.xrplApi);
457
-
458
- return this.xrplAcc.makePayment(this.registryAddress,
459
- XrplConstants.MIN_XRP_AMOUNT,
460
- XrplConstants.XRP,
461
- null,
462
- [
463
- { type: MemoTypes.HOST_REBATE, format: "", data: "" },
464
- { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
465
- ],
466
- options.transactionOptions);
467
- }
468
-
469
- getLeaseNFTokenIdPrefix() {
470
- let buf = Buffer.allocUnsafe(24);
471
- buf.writeUInt16BE(1);
472
- buf.writeUInt16BE(0, 2);
473
- codec.decodeAccountID(this.xrplAcc.address).copy(buf, 4);
474
- return buf.toString('hex');
475
- }
476
-
477
- async transfer(transfereeAddress = this.xrplAcc.address, options = {}) {
478
- if (!(await this.isRegistered()))
479
- throw "Host is not registered.";
480
-
481
- const transfereeAcc = new XrplAccount(transfereeAddress, null, { xrplApi: this.xrplApi });
482
-
483
- if (this.xrplAcc.address !== transfereeAddress) {
484
- // Find the new transferee also owns an Evernode Host Registration NFT.
485
- const nft = (await transfereeAcc.getNfts()).find(n => n.URI.startsWith(EvernodeConstants.NFT_PREFIX_HEX) && n.Issuer === this.registryAddress);
486
- if (nft) {
487
- // Check whether the token was actually issued from Evernode registry contract.
488
- const issuerHex = nft.NFTokenID.substr(8, 40);
489
- const issuerAddr = codec.encodeAccountID(Buffer.from(issuerHex, 'hex'));
490
- if (issuerAddr == this.registryAddress) {
491
- throw "The transferee is already registered in Evernode.";
492
- }
493
- }
494
- }
495
-
496
- let memoData = Buffer.allocUnsafe(20);
497
- codec.decodeAccountID(transfereeAddress).copy(memoData);
498
-
499
- // To obtain registration NFT Page Keylet and index.
500
- const regNFT = await this.getRegistrationNft();
501
- const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, this.xrplAcc, this.xrplApi);
502
-
503
- await this.xrplAcc.makePayment(this.registryAddress,
504
- XrplConstants.MIN_XRP_AMOUNT,
505
- XrplConstants.XRP,
506
- null,
507
- [
508
- { type: MemoTypes.HOST_TRANSFER, format: MemoFormats.HEX, data: memoData.toString('hex') },
509
- { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
510
- ],
511
- options.transactionOptions);
512
-
513
- let offer = null;
514
- let attempts = 0;
515
- let offerLedgerIndex = 0;
516
- const regAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
517
-
518
- while (attempts < OFFER_WAIT_TIMEOUT) {
519
- offer = (await regAcc.getNftOffers()).find(o => (o.NFTokenID == regNFT.NFTokenID) && (o.Flags === 0));
520
- offerLedgerIndex = this.xrplApi.ledgerIndex;
521
- if (offer)
522
- break;
523
- await new Promise(resolve => setTimeout(resolve, 1000));
524
- attempts++;
525
- }
526
- if (!offer)
527
- throw 'No buy offer found within timeout.';
528
-
529
- console.log('Accepting the buy offer..');
530
-
531
- // Wait until the next ledger after the offer is created.
532
- // Otherwise if the offer accepted in the same legder which it's been created,
533
- // We cannot fetch the offer from registry contract event handler since it's getting deleted immediately.
534
- await new Promise(async resolve => {
535
- while (this.xrplApi.ledgerIndex <= offerLedgerIndex)
536
- await new Promise(resolve2 => setTimeout(resolve2, 1000));
537
- resolve();
538
- });
539
-
540
- await this.xrplAcc.sellNft(offer.index);
541
- }
542
-
543
- async isTransferee() {
544
-
545
- // Check the availability of TRANSFEREE state for this host address.
546
- const stateTransfereeAddrKey = StateHelpers.generateTransfereeAddrStateKey(this.xrplAcc.address);
547
- const stateTransfereeAddrIndex = StateHelpers.getHookStateIndex(this.registryAddress, stateTransfereeAddrKey);
548
- const res = await this.xrplApi.getLedgerEntry(stateTransfereeAddrIndex);
549
-
550
- if (res && res?.HookStateData)
551
- return true;
552
-
553
- return false;
554
- }
555
- }
556
-
557
- module.exports = {
558
- HostEvents,
559
- HostClient
560
- }
@@ -1,54 +0,0 @@
1
- const { EvernodeEvents } = require('../evernode-common');
2
- const { BaseEvernodeClient } = require('./base-evernode-client');
3
- const { DefaultValues } = require('../defaults');
4
-
5
- const RegistryEvents = {
6
- HostRegistered: EvernodeEvents.HostRegistered,
7
- HostDeregistered: EvernodeEvents.HostDeregistered,
8
- HostRegUpdated: EvernodeEvents.HostRegUpdated,
9
- RegistryInitialized: EvernodeEvents.RegistryInitialized,
10
- Heartbeat: EvernodeEvents.Heartbeat,
11
- HostPostDeregistered: EvernodeEvents.HostPostDeregistered,
12
- DeadHostPrune: EvernodeEvents.DeadHostPrune,
13
- HostTransfer: EvernodeEvents.HostTransfer,
14
- HostRebate: EvernodeEvents.HostRebate
15
- }
16
-
17
- class RegistryClient extends BaseEvernodeClient {
18
-
19
- /**
20
- * Constructs a registry client instance.
21
- * @param {object} options [Optional] An object with 'rippledServer' URL and 'registryAddress'.
22
- */
23
- constructor(options = {}) {
24
- super((options.registryAddress || DefaultValues.registryAddress), null, Object.values(RegistryEvents), false, options);
25
- }
26
-
27
- /**
28
- * Gets all the active hosts registered in Evernode without paginating.
29
- * @returns The list of active hosts.
30
- */
31
- async getActiveHosts() {
32
- let fullHostList = [];
33
- const hosts = await this.getHosts();
34
- if (hosts.nextPageToken) {
35
- let currentPageToken = hosts.nextPageToken;
36
- let nextHosts = null;
37
- fullHostList = fullHostList.concat(hosts.data);
38
- while (currentPageToken) {
39
- nextHosts = await this.getHosts(null, null, currentPageToken);
40
- fullHostList = fullHostList.concat(nextHosts.nextPageToken ? nextHosts.data : nextHosts);
41
- currentPageToken = nextHosts.nextPageToken;
42
- }
43
- } else {
44
- fullHostList = fullHostList.concat(hosts);
45
- }
46
- // Filter only active hosts.
47
- return fullHostList.filter(h => h.active);
48
- }
49
- }
50
-
51
- module.exports = {
52
- RegistryEvents,
53
- RegistryClient
54
- }