evernode-js-client 0.5.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,357 +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
-
11
- const OFFER_WAIT_TIMEOUT = 60;
12
-
13
- const HostEvents = {
14
- AcquireLease: EvernodeEvents.AcquireLease,
15
- ExtendLease: EvernodeEvents.ExtendLease
16
- }
17
-
18
- class HostClient extends BaseEvernodeClient {
19
-
20
- constructor(xrpAddress, xrpSecret, options = {}) {
21
- super(xrpAddress, xrpSecret, Object.values(HostEvents), true, options);
22
- }
23
-
24
- async getRegistrationNft() {
25
- // Find an owned NFT with matching Evernode host NFT prefix.
26
- const nft = (await this.xrplAcc.getNfts()).find(n => n.URI.startsWith(EvernodeConstants.NFT_PREFIX_HEX) && n.Issuer === this.registryAddress);
27
- if (nft) {
28
- // Check whether the token was actually issued from Evernode registry contract.
29
- const issuerHex = nft.NFTokenID.substr(8, 40);
30
- const issuerAddr = codec.encodeAccountID(Buffer.from(issuerHex, 'hex'));
31
- if (issuerAddr == this.registryAddress) {
32
- return nft;
33
- }
34
- }
35
-
36
- return null;
37
- }
38
-
39
- async getRegistration() {
40
- // Check whether we own an evernode host token.
41
- const nft = await this.getRegistrationNft();
42
- if (nft) {
43
- const host = await this.getHostInfo();
44
- return (host?.nfTokenId == nft.NFTokenID) ? host : null;
45
- }
46
-
47
- return null;
48
- }
49
-
50
- async getLeaseOffers() {
51
- return await EvernodeHelpers.getLeaseOffers(this.xrplAcc);
52
- }
53
-
54
- async cancelOffer(offerIndex) {
55
- return this.xrplAcc.cancelOffer(offerIndex);
56
- }
57
-
58
- async isRegistered() {
59
- return (await this.getRegistration()) !== null;
60
- }
61
-
62
- async prepareAccount(domain) {
63
- const [flags, trustLines, msgKey, curDomain] = await Promise.all([
64
- this.xrplAcc.getFlags(),
65
- this.xrplAcc.getTrustLines(EvernodeConstants.EVR, this.config.evrIssuerAddress),
66
- this.xrplAcc.getMessageKey(),
67
- this.xrplAcc.getDomain()]);
68
-
69
- let accountSetFields = {};
70
- accountSetFields = (!flags.lsfDefaultRipple) ? { ...accountSetFields, Flags: { asfDefaultRipple: true } } : accountSetFields;
71
- accountSetFields = (!msgKey) ? { ...accountSetFields, MessageKey: this.accKeyPair.publicKey } : accountSetFields;
72
-
73
- domain = domain.toLowerCase();
74
- accountSetFields = (!curDomain || curDomain !== domain) ?
75
- { ...accountSetFields, Domain: domain } : accountSetFields;
76
-
77
- if (Object.keys(accountSetFields).length !== 0)
78
- await this.xrplAcc.setAccountFields(accountSetFields);
79
-
80
- if (trustLines.length === 0)
81
- await this.xrplAcc.setTrustLine(EvernodeConstants.EVR, this.config.evrIssuerAddress, "99999999999999");
82
- }
83
-
84
- async offerLease(leaseIndex, leaseAmount, tosHash) {
85
- // <prefix><lease index 16)><half of tos hash><lease amount (uint32)>
86
- const prefixLen = EvernodeConstants.LEASE_NFT_PREFIX_HEX.length / 2;
87
- const halfToSLen = tosHash.length / 4;
88
- const uriBuf = Buffer.allocUnsafe(prefixLen + halfToSLen + 10);
89
- Buffer.from(EvernodeConstants.LEASE_NFT_PREFIX_HEX, 'hex').copy(uriBuf);
90
- uriBuf.writeUInt16BE(leaseIndex, prefixLen);
91
- Buffer.from(tosHash, 'hex').copy(uriBuf, prefixLen + 2, 0, halfToSLen);
92
- uriBuf.writeBigInt64BE(XflHelpers.getXfl(leaseAmount.toString()), prefixLen + 2 + halfToSLen);
93
- const uri = uriBuf.toString('hex').toUpperCase();
94
-
95
- await this.xrplAcc.mintNft(uri, 0, 0, { isBurnable: true, isHexUri: true });
96
-
97
- const nft = await this.xrplAcc.getNftByUri(uri, true);
98
- if (!nft)
99
- throw "Offer lease NFT creation error.";
100
-
101
- await this.xrplAcc.offerSellNft(nft.NFTokenID,
102
- leaseAmount.toString(),
103
- EvernodeConstants.EVR,
104
- this.config.evrIssuerAddress);
105
- }
106
-
107
- async expireLease(nfTokenId, tenantAddress = null) {
108
- await this.xrplAcc.burnNft(nfTokenId, tenantAddress);
109
- }
110
-
111
- async register(countryCode, cpuMicroSec, ramMb, diskMb, totalInstanceCount, cpuModel, cpuCount, cpuSpeed, description, options = {}) {
112
- if (!/^([A-Z]{2})$/.test(countryCode))
113
- throw "countryCode should consist of 2 uppercase alphabetical characters";
114
- else if (!cpuMicroSec || isNaN(cpuMicroSec) || cpuMicroSec % 1 != 0 || cpuMicroSec < 0)
115
- throw "cpuMicroSec should be a positive integer";
116
- else if (!ramMb || isNaN(ramMb) || ramMb % 1 != 0 || ramMb < 0)
117
- throw "ramMb should be a positive integer";
118
- else if (!diskMb || isNaN(diskMb) || diskMb % 1 != 0 || diskMb < 0)
119
- throw "diskMb should be a positive integer";
120
- else if (!totalInstanceCount || isNaN(totalInstanceCount) || totalInstanceCount % 1 != 0 || totalInstanceCount < 0)
121
- throw "totalInstanceCount should be a positive intiger";
122
- else if (!cpuCount || isNaN(cpuCount) || cpuCount % 1 != 0 || cpuCount < 0)
123
- throw "CPU count should be a positive integer";
124
- else if (!cpuSpeed || isNaN(cpuSpeed) || cpuSpeed % 1 != 0 || cpuSpeed < 0)
125
- throw "CPU speed should be a positive integer";
126
- else if (!cpuModel)
127
- throw "cpu model cannot be empty";
128
-
129
- // Need to use control characters inside this regex to match ascii characters.
130
- // Here we allow all the characters in ascii range except ";" for the description.
131
- // no-control-regex is enabled default by eslint:recommended, So we disable it only for next line.
132
- // eslint-disable-next-line no-control-regex
133
- else if (!/^((?![;])[\x00-\x7F]){0,26}$/.test(description))
134
- throw "description should consist of 0-26 ascii characters except ';'";
135
-
136
- if (await this.isRegistered())
137
- throw "Host already registered.";
138
-
139
- // Check whether is there any missed NFT sell offer that needs to be accepted
140
- // from the client-side in order to complete the registration.
141
- const regNft = await this.getRegistrationNft();
142
- if (!regNft) {
143
- const regInfo = await this.getHostInfo(this.xrplAcc.address);
144
- if (regInfo) {
145
- const registryAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
146
- const sellOffer = (await registryAcc.getNftOffers()).find(o => o.NFTokenID == regInfo.nfTokenId);
147
- if (sellOffer) {
148
- await this.xrplAcc.buyNft(sellOffer.index);
149
- console.log("Registration was successfully completed after acquiring the NFT.");
150
- return await this.isRegistered();
151
- }
152
- }
153
- }
154
-
155
- const memoData = `${countryCode};${cpuMicroSec};${ramMb};${diskMb};${totalInstanceCount};${cpuModel};${cpuCount};${cpuSpeed};${description}`
156
- const tx = await this.xrplAcc.makePayment(this.registryAddress,
157
- this.config.hostRegFee.toString(),
158
- EvernodeConstants.EVR,
159
- this.config.evrIssuerAddress,
160
- [{ type: MemoTypes.HOST_REG, format: MemoFormats.TEXT, data: memoData }],
161
- options.transactionOptions);
162
-
163
- console.log('Waiting for the sell offer')
164
- const regAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
165
- let offer = null;
166
- let attempts = 0;
167
- let offerLedgerIndex = 0;
168
- while (attempts < OFFER_WAIT_TIMEOUT) {
169
- const nft = (await regAcc.getNfts()).find(n => n.URI === `${EvernodeConstants.NFT_PREFIX_HEX}${tx.id}`);
170
- if (nft) {
171
- offer = (await regAcc.getNftOffers()).find(o => o.Destination === this.xrplAcc.address && o.NFTokenID === nft.NFTokenID && o.Flags === 1);
172
- offerLedgerIndex = this.xrplApi.ledgerIndex;
173
- if (offer)
174
- break;
175
- await new Promise(resolve => setTimeout(resolve, 1000));
176
- attempts++;
177
- }
178
-
179
- }
180
- if (!offer)
181
- throw 'No sell offer found within timeout.';
182
-
183
- console.log('Accepting the sell offer..');
184
-
185
- // Wait until the next ledger after the offer is created.
186
- // Otherwise if the offer accepted in the same legder which it's been created,
187
- // We cannot fetch the offer from registry contract event handler since it's getting deleted immediately.
188
- await new Promise(async resolve => {
189
- while (this.xrplApi.ledgerIndex <= offerLedgerIndex)
190
- await new Promise(resolve2 => setTimeout(resolve2, 1000));
191
- resolve();
192
- });
193
-
194
- await this.xrplAcc.buyNft(offer.index);
195
-
196
- return await this.isRegistered();
197
- }
198
-
199
- async deregister(options = {}) {
200
-
201
- if (!(await this.isRegistered()))
202
- throw "Host not registered."
203
-
204
- const regNFT = await this.getRegistrationNft();
205
- await this.xrplAcc.makePayment(this.registryAddress,
206
- XrplConstants.MIN_XRP_AMOUNT,
207
- XrplConstants.XRP,
208
- null,
209
- [{ type: MemoTypes.HOST_DEREG, format: MemoFormats.HEX, data: regNFT.NFTokenID }],
210
- options.transactionOptions);
211
-
212
- console.log('Waiting for the buy offer')
213
- const regAcc = new XrplAccount(this.registryAddress, null, { xrplApi: this.xrplApi });
214
- let offer = null;
215
- let attempts = 0;
216
- let offerLedgerIndex = 0;
217
- while (attempts < OFFER_WAIT_TIMEOUT) {
218
- offer = (await regAcc.getNftOffers()).find(o => (o.NFTokenID == regNFT.NFTokenID) && (o.Flags === 0));
219
- offerLedgerIndex = this.xrplApi.ledgerIndex;
220
- if (offer)
221
- break;
222
- await new Promise(resolve => setTimeout(resolve, 1000));
223
- attempts++;
224
- }
225
- if (!offer)
226
- throw 'No buy offer found within timeout.';
227
-
228
- console.log('Accepting the buy offer..');
229
-
230
- // Wait until the next ledger after the offer is created.
231
- // Otherwise if the offer accepted in the same legder which it's been created,
232
- // We cannot fetch the offer from registry contract event handler since it's getting deleted immediately.
233
- await new Promise(async resolve => {
234
- while (this.xrplApi.ledgerIndex <= offerLedgerIndex)
235
- await new Promise(resolve2 => setTimeout(resolve2, 1000));
236
- resolve();
237
- });
238
-
239
- await this.xrplAcc.sellNft(
240
- offer.index,
241
- [{ type: MemoTypes.HOST_POST_DEREG, format: MemoFormats.HEX, data: regNFT.NFTokenID }]
242
- );
243
-
244
- return await this.isRegistered();
245
- }
246
-
247
- async updateRegInfo(activeInstanceCount = null, version = null, totalInstanceCount = null, tokenID = null, countryCode = null, cpuMicroSec = null, ramMb = null, diskMb = null, description = null, options = {}) {
248
- const dataStr = `${tokenID ? tokenID : ''};${countryCode ? countryCode : ''};${cpuMicroSec ? cpuMicroSec : ''};${ramMb ? ramMb : ''};${diskMb ? diskMb : ''};${totalInstanceCount ? totalInstanceCount : ''};${activeInstanceCount !== undefined ? activeInstanceCount : ''};${description ? description : ''};${version ? version : ''}`;
249
- return await this.xrplAcc.makePayment(this.registryAddress,
250
- XrplConstants.MIN_XRP_AMOUNT,
251
- XrplConstants.XRP,
252
- null,
253
- [{ type: MemoTypes.HOST_UPDATE_INFO, format: MemoFormats.TEXT, data: dataStr }],
254
- options.transactionOptions);
255
- }
256
-
257
- async heartbeat(options = {}) {
258
- return this.xrplAcc.makePayment(this.registryAddress,
259
- XrplConstants.MIN_XRP_AMOUNT,
260
- XrplConstants.XRP,
261
- null,
262
- [{ type: MemoTypes.HEARTBEAT, format: "", data: "" }],
263
- options.transactionOptions);
264
- }
265
-
266
- async acquireSuccess(txHash, tenantAddress, instanceInfo, options = {}) {
267
-
268
- // Encrypt the instance info with the tenant's encryption key (Specified in MessageKey field of the tenant account).
269
- const tenantAcc = new XrplAccount(tenantAddress, null, { xrplApi: this.xrplApi });
270
- const encKey = await tenantAcc.getMessageKey();
271
- if (!encKey)
272
- throw "Tenant encryption key not set.";
273
-
274
- const encrypted = await EncryptionHelper.encrypt(encKey, instanceInfo);
275
- const memos = [
276
- { type: MemoTypes.ACQUIRE_SUCCESS, format: MemoFormats.BASE64, data: encrypted },
277
- { type: MemoTypes.ACQUIRE_REF, format: MemoFormats.HEX, data: txHash }];
278
-
279
- return this.xrplAcc.makePayment(tenantAddress,
280
- XrplConstants.MIN_XRP_AMOUNT,
281
- XrplConstants.XRP,
282
- null,
283
- memos,
284
- options.transactionOptions);
285
- }
286
-
287
- async acquireError(txHash, tenantAddress, leaseAmount, reason, options = {}) {
288
-
289
- const memos = [
290
- { type: MemoTypes.ACQUIRE_ERROR, format: MemoFormats.JSON, data: { type: ErrorCodes.ACQUIRE_ERR, reason: reason } },
291
- { type: MemoTypes.ACQUIRE_REF, format: MemoFormats.HEX, data: txHash }];
292
-
293
- return this.xrplAcc.makePayment(tenantAddress,
294
- leaseAmount.toString(),
295
- EvernodeConstants.EVR,
296
- this.config.evrIssuerAddress,
297
- memos,
298
- options.transactionOptions);
299
- }
300
-
301
- async extendSuccess(txHash, tenantAddress, expiryMoment, options = {}) {
302
- let buf = Buffer.allocUnsafe(4);
303
- buf.writeUInt32BE(expiryMoment);
304
-
305
- const memos = [
306
- { type: MemoTypes.EXTEND_SUCCESS, format: MemoFormats.HEX, data: buf.toString('hex') },
307
- { type: MemoTypes.EXTEND_REF, format: MemoFormats.HEX, data: txHash }];
308
-
309
- return this.xrplAcc.makePayment(tenantAddress,
310
- XrplConstants.MIN_XRP_AMOUNT,
311
- XrplConstants.XRP,
312
- null,
313
- memos,
314
- options.transactionOptions);
315
- }
316
-
317
- async extendError(txHash, tenantAddress, reason, refund, options = {}) {
318
-
319
- const memos = [
320
- { type: MemoTypes.EXTEND_ERROR, format: MemoFormats.JSON, data: { type: ErrorCodes.EXTEND_ERR, reason: reason } },
321
- { type: MemoTypes.EXTEND_REF, format: MemoFormats.HEX, data: txHash }];
322
-
323
- // Required to refund the paid EVR amount as the offer extention is not successfull.
324
- return this.xrplAcc.makePayment(tenantAddress,
325
- refund.toString(),
326
- EvernodeConstants.EVR,
327
- this.config.evrIssuerAddress,
328
- memos,
329
- options.transactionOptions);
330
- }
331
-
332
- async refundTenant(txHash, tenantAddress, refundAmount, options = {}) {
333
- const memos = [
334
- { type: MemoTypes.REFUND, format: '', data: '' },
335
- { type: MemoTypes.REFUND_REF, format: MemoFormats.HEX, data: txHash }];
336
-
337
- return this.xrplAcc.makePayment(tenantAddress,
338
- refundAmount.toString(),
339
- EvernodeConstants.EVR,
340
- this.config.evrIssuerAddress,
341
- memos,
342
- options.transactionOptions);
343
- }
344
-
345
- getLeaseNFTokenIdPrefix() {
346
- let buf = Buffer.allocUnsafe(24);
347
- buf.writeUInt16BE(1);
348
- buf.writeUInt16BE(0, 2);
349
- codec.decodeAccountID(this.xrplAcc.address).copy(buf, 4);
350
- return buf.toString('hex');
351
- }
352
- }
353
-
354
- module.exports = {
355
- HostEvents,
356
- HostClient
357
- }
@@ -1,52 +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
- }
14
-
15
- class RegistryClient extends BaseEvernodeClient {
16
-
17
- /**
18
- * Constructs a registry client instance.
19
- * @param {object} options [Optional] An object with 'rippledServer' URL and 'registryAddress'.
20
- */
21
- constructor(options = {}) {
22
- super((options.registryAddress || DefaultValues.registryAddress), null, Object.values(RegistryEvents), false, options);
23
- }
24
-
25
- /**
26
- * Gets all the active hosts registered in Evernode without paginating.
27
- * @returns The list of active hosts.
28
- */
29
- async getActiveHosts() {
30
- let fullHostList = [];
31
- const hosts = await this.getHosts();
32
- if (hosts.nextPageToken) {
33
- let currentPageToken = hosts.nextPageToken;
34
- let nextHosts = null;
35
- fullHostList = fullHostList.concat(hosts.data);
36
- while (currentPageToken) {
37
- nextHosts = await this.getHosts(null, null, currentPageToken);
38
- fullHostList = fullHostList.concat(nextHosts.nextPageToken ? nextHosts.data : nextHosts);
39
- currentPageToken = nextHosts.nextPageToken;
40
- }
41
- } else {
42
- fullHostList = fullHostList.concat(hosts);
43
- }
44
- // Filter only active hosts.
45
- return fullHostList.filter(h => h.active);
46
- }
47
- }
48
-
49
- module.exports = {
50
- RegistryEvents,
51
- RegistryClient
52
- }
@@ -1,264 +0,0 @@
1
- const { BaseEvernodeClient } = require('./base-evernode-client');
2
- const { EvernodeEvents, MemoFormats, MemoTypes, ErrorCodes, ErrorReasons, EvernodeConstants } = require('../evernode-common');
3
- const { EncryptionHelper } = require('../encryption-helper');
4
- const { XrplAccount } = require('../xrpl-account');
5
- const { UtilHelpers } = require('../util-helpers');
6
- const { Buffer } = require('buffer');
7
- const codec = require('ripple-address-codec');
8
- const { EvernodeHelpers } = require('../evernode-helpers');
9
- const { TransactionHelper } = require('../transaction-helper');
10
-
11
- const DEFAULT_WAIT_TIMEOUT = 60000;
12
-
13
- const TenantEvents = {
14
- AcquireSuccess: EvernodeEvents.AcquireSuccess,
15
- AcquireError: EvernodeEvents.AcquireError,
16
- ExtendSuccess: EvernodeEvents.ExtendSuccess,
17
- ExtendError: EvernodeEvents.ExtendError,
18
- }
19
-
20
- class TenantClient extends BaseEvernodeClient {
21
-
22
- /**
23
- * Constructs a tenant client instance.
24
- * @param {string} xrpAddress XRPL address of the tenant.
25
- * @param {string} XRPL secret of the tenant.
26
- * @param {object} options [Optional] An object with 'rippledServer' URL and 'registryAddress'.
27
- */
28
- constructor(xrpAddress, xrpSecret, options = {}) {
29
- super(xrpAddress, xrpSecret, Object.values(TenantEvents), false, options);
30
- }
31
-
32
- async prepareAccount() {
33
- try {
34
- if (!await this.xrplAcc.getMessageKey())
35
- await this.xrplAcc.setAccountFields({ MessageKey: this.accKeyPair.publicKey });
36
- }
37
- catch (err) {
38
- console.log("Error in preparing user xrpl account for Evernode.", err);
39
- }
40
- }
41
-
42
- async getLeaseHost(hostAddress) {
43
- const host = new XrplAccount(hostAddress, null, { xrplApi: this.xrplApi });
44
- // Find an owned NFT with matching Evernode host NFT prefix.
45
- const nft = (await host.getNfts()).find(n => n.URI.startsWith(EvernodeConstants.NFT_PREFIX_HEX));
46
- if (!nft)
47
- throw { reason: ErrorReasons.HOST_INVALID, error: "Host is not registered." };
48
-
49
- // Check whether the token was actually issued from Evernode registry contract.
50
- const issuerHex = nft.NFTokenID.substr(8, 40);
51
- const issuerAddr = codec.encodeAccountID(Buffer.from(issuerHex, 'hex'));
52
- if (issuerAddr != this.registryAddress)
53
- throw { reason: ErrorReasons.HOST_INVALID, error: "Host is not registered." };
54
-
55
- // Check whether active.
56
- const hostInfo = await this.getHostInfo(host.address);
57
- if (!hostInfo)
58
- throw { reason: ErrorReasons.HOST_INVALID, error: "Host is not registered." };
59
- else if (!hostInfo.active)
60
- throw { reason: ErrorReasons.HOST_INACTIVE, error: "Host is not active." };
61
-
62
- return host;
63
- }
64
-
65
- /**
66
- *
67
- * @param {string} hostAddress XRPL address of the host to acquire the lease.
68
- * @param {object} requirement The instance requirements and configuration.
69
- * @param {object} options [Optional] Options for the XRPL transaction.
70
- * @returns The transaction result.
71
- */
72
- async acquireLeaseSubmit(hostAddress, requirement, options = {}) {
73
-
74
- const hostAcc = await this.getLeaseHost(hostAddress);
75
- let selectedOfferIndex = options.leaseOfferIndex;
76
-
77
- // Attempt to get first available offer, if offer is not specified in options.
78
- if (!selectedOfferIndex) {
79
- const nftOffers = await EvernodeHelpers.getLeaseOffers(hostAcc);
80
- selectedOfferIndex = nftOffers && nftOffers[0] && nftOffers[0].index;
81
-
82
- if (!selectedOfferIndex)
83
- throw { reason: ErrorReasons.NO_OFFER, error: "No offers available." };
84
- }
85
-
86
- // Encrypt the requirements with the host's encryption key (Specified in MessageKey field of the host account).
87
- const encKey = await hostAcc.getMessageKey();
88
- if (!encKey)
89
- throw { reason: ErrorReasons.INTERNAL_ERR, error: "Host encryption key not set." };
90
-
91
- const ecrypted = await EncryptionHelper.encrypt(encKey, requirement, {
92
- iv: options.iv, // Must be null or 16 bytes.
93
- ephemPrivateKey: options.ephemPrivateKey // Must be null or 32 bytes.
94
- });
95
-
96
- return this.xrplAcc.buyNft(selectedOfferIndex, [{ type: MemoTypes.ACQUIRE_LEASE, format: MemoFormats.BASE64, data: ecrypted }], options.transactionOptions);
97
- }
98
-
99
- /**
100
- * Watch for the acquire-success response after the acquire request is made.
101
- * @param {object} tx The transaction returned by the acquireLeaseSubmit function.
102
- * @param {object} options [Optional] Options for the XRPL transaction.
103
- * @returns An object including transaction details,instance info, and acquireReference Id.
104
- */
105
- async watchAcquireResponse(tx, options = {}) {
106
- console.log(`Waiting for acquire response... (txHash: ${tx.id})`);
107
-
108
- const failTimeout = setTimeout(() => {
109
- throw({ error: ErrorCodes.ACQUIRE_ERR, reason: ErrorReasons.TIMEOUT });
110
- }, options.timeout || DEFAULT_WAIT_TIMEOUT);
111
-
112
- let relevantTx = null;
113
- while (!relevantTx) {
114
- const txList = await this.xrplAcc.getAccountTrx(tx.details.ledger_index);
115
- for (let t of txList) {
116
- t.tx.Memos = TransactionHelper.deserializeMemos(t.tx?.Memos);
117
- const res = await this.extractEvernodeEvent(t.tx);
118
- if ((res?.name === EvernodeEvents.AcquireSuccess || res?.name === EvernodeEvents.AcquireError) && res?.data?.acquireRefId === tx.id) {
119
- clearTimeout(failTimeout);
120
- relevantTx = res;
121
- break;
122
- }
123
- }
124
- await new Promise(resolve => setTimeout(resolve, 2000));
125
- }
126
-
127
- if (relevantTx?.name === TenantEvents.AcquireSuccess) {
128
- return({
129
- transaction: relevantTx?.data.transaction,
130
- instance: relevantTx?.data.payload.content,
131
- acquireRefId: relevantTx?.data.acquireRefId
132
- });
133
- } else if (relevantTx?.name === TenantEvents.AcquireError) {
134
- throw({
135
- error: ErrorCodes.ACQUIRE_ERR,
136
- transaction: relevantTx?.data.transaction,
137
- reason: relevantTx?.data.reason,
138
- acquireRefId: relevantTx?.data.acquireRefId
139
- })
140
- }
141
- }
142
-
143
- /**
144
- * Acquire an instance from a host
145
- * @param {string} hostAddress XRPL address of the host to acquire the lease.
146
- * @param {object} requirement The instance requirements and configuration.
147
- * @param {object} options [Optional] Options for the XRPL transaction.
148
- * @returns An object including transaction details,instance info, and acquireReference Id.
149
- */
150
- acquireLease(hostAddress, requirement, options = {}) {
151
- return new Promise(async (resolve, reject) => {
152
- const tx = await this.acquireLeaseSubmit(hostAddress, requirement, options).catch(error => {
153
- reject({ error: ErrorCodes.ACQUIRE_ERR, reason: error.reason || ErrorReasons.TRANSACTION_FAILURE, content: error.error || error });
154
- });
155
- if (tx) {
156
- try {
157
- const response = await this.watchAcquireResponse(tx, options);
158
- resolve(response);
159
- } catch (error) {
160
- reject(error);
161
- }
162
- }
163
- });
164
- }
165
-
166
- /**
167
- * This function is called by a tenant client to submit the extend lease transaction in certain host. This function will be called inside extendLease function. This function can take four parameters as follows.
168
- * @param {string} hostAddress XRPL account address of the host.
169
- * @param {number} amount Cost for the extended moments , in EVRs.
170
- * @param {string} tokenID Tenant received instance name. this name can be retrieve by performing acquire Lease.
171
- * @param {object} options This is an optional field and contains necessary details for the transactions.
172
- * @returns The transaction result.
173
- */
174
- async extendLeaseSubmit(hostAddress, amount, tokenID, options = {}) {
175
- const host = await this.getLeaseHost(hostAddress);
176
- return this.xrplAcc.makePayment(host.address, amount.toString(), EvernodeConstants.EVR, this.config.evrIssuerAddress,
177
- [{ type: MemoTypes.EXTEND_LEASE, format: MemoFormats.HEX, data: tokenID }], options.transactionOptions);
178
- }
179
-
180
- /**
181
- * This function watches for an extendlease-success response(transaction) and returns the response or throws the error response on extendlease-error response from the host XRPL account. This function is called within the extendLease function.
182
- * @param {object} tx Response of extendLeaseSubmit.
183
- * @param {object} options This is an optional field and contains necessary details for the transactions.
184
- * @returns An object including transaction details.
185
- */
186
- async watchExtendResponse(tx, options = {}) {
187
- console.log(`Waiting for extend lease response... (txHash: ${tx.id})`);
188
-
189
- const failTimeout = setTimeout(() => {
190
- throw({ error: ErrorCodes.EXTEND_ERR, reason: ErrorReasons.TIMEOUT });
191
- }, options.timeout || DEFAULT_WAIT_TIMEOUT);
192
-
193
- let relevantTx = null;
194
- while (!relevantTx) {
195
- const txList = await this.xrplAcc.getAccountTrx(tx.details.ledger_index);
196
- for (let t of txList) {
197
- t.tx.Memos = TransactionHelper.deserializeMemos(t.tx.Memos);
198
- const res = await this.extractEvernodeEvent(t.tx);
199
- if ((res?.name === TenantEvents.ExtendSuccess || res?.name === TenantEvents.ExtendError) && res?.data?.extendRefId === tx.id) {
200
- clearTimeout(failTimeout);
201
- relevantTx = res;
202
- break;
203
- }
204
- }
205
- await new Promise(resolve => setTimeout(resolve, 1000));
206
- }
207
-
208
- if (relevantTx?.name === TenantEvents.ExtendSuccess) {
209
- return({
210
- transaction: relevantTx?.data.transaction,
211
- expiryMoment: relevantTx?.data.expiryMoment,
212
- extendeRefId: relevantTx?.data.extendRefId
213
- });
214
- } else if (relevantTx?.name === TenantEvents.ExtendError) {
215
- throw({
216
- error: ErrorCodes.EXTEND_ERR,
217
- transaction: relevantTx?.data.transaction,
218
- reason: relevantTx?.data.reason
219
- })
220
- }
221
- }
222
-
223
- /**
224
- * This function is called by a tenant client to extend an available instance in certain host. This function can take four parameters as follows.
225
- * @param {string} hostAddress XRPL account address of the host.
226
- * @param {number} moments 1190 ledgers (est. 1 hour).
227
- * @param {string} instanceName Tenant received instance name. this name can be retrieve by performing acquire Lease.
228
- * @param {object} options This is an optional field and contains necessary details for the transactions.
229
- * @returns An object including transaction details.
230
- */
231
- extendLease(hostAddress, moments, instanceName, options = {}) {
232
- return new Promise(async (resolve, reject) => {
233
- const tokenID = instanceName;
234
- const nft = (await this.xrplAcc.getNfts())?.find(n => n.NFTokenID == tokenID);
235
-
236
- if (!nft) {
237
- reject({ error: ErrorCodes.EXTEND_ERR, reason: ErrorReasons.NO_NFT, content: 'Could not find the nft for lease extend request.' });
238
- return;
239
- }
240
-
241
- let minLedgerIndex = this.xrplApi.ledgerIndex;
242
-
243
- // Get the agreement lease amount from the nft and calculate EVR amount to be sent.
244
- const uriInfo = UtilHelpers.decodeLeaseNftUri(nft.URI);
245
- const tx = await this.extendLeaseSubmit(hostAddress, moments * uriInfo.leaseAmount, tokenID, options).catch(error => {
246
- reject({ error: ErrorCodes.EXTEND_ERR, reason: error.reason || ErrorReasons.TRANSACTION_FAILURE, content: error.error || error });
247
- });
248
-
249
- if (tx) {
250
- try {
251
- const response = await this.watchExtendResponse(tx, minLedgerIndex, options)
252
- resolve(response);
253
- } catch (error) {
254
- reject(error);
255
- }
256
- }
257
- });
258
- }
259
- }
260
-
261
- module.exports = {
262
- TenantEvents,
263
- TenantClient
264
- }