evernode-js-client 0.5.9 → 0.5.10

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.
Files changed (2) hide show
  1. package/index.js +262 -47
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -11732,6 +11732,7 @@ const { EventEmitter } = __nccwpck_require__(6170);
11732
11732
  const { UtilHelpers } = __nccwpck_require__(6687);
11733
11733
  const { FirestoreHandler } = __nccwpck_require__(9718);
11734
11734
  const { StateHelpers } = __nccwpck_require__(3860);
11735
+ const { EvernodeHelpers } = __nccwpck_require__(2523);
11735
11736
 
11736
11737
  class BaseEvernodeClient {
11737
11738
 
@@ -12037,17 +12038,13 @@ class BaseEvernodeClient {
12037
12038
  }
12038
12039
  }
12039
12040
  else if (tx.Memos.length >= 1 &&
12040
- tx.Memos[0].type === MemoTypes.HOST_REG && tx.Memos[0].format === MemoFormats.TEXT && tx.Memos[0].data) {
12041
+ tx.Memos[0].type === MemoTypes.HOST_REG && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
12041
12042
 
12042
- const parts = tx.Memos[0].data.split(';');
12043
12043
  return {
12044
12044
  name: EvernodeEvents.HostRegistered,
12045
12045
  data: {
12046
12046
  transaction: tx,
12047
- host: tx.Account,
12048
- token: parts[0],
12049
- instanceSize: parts[1],
12050
- location: parts[2]
12047
+ host: tx.Account
12051
12048
  }
12052
12049
  }
12053
12050
  }
@@ -12135,17 +12132,13 @@ class BaseEvernodeClient {
12135
12132
  }
12136
12133
  }
12137
12134
  else if (tx.Memos.length >= 1 &&
12138
- tx.Memos[0].type === MemoTypes.HOST_UPDATE_INFO && tx.Memos[0].format === MemoFormats.TEXT && tx.Memos[0].data) {
12139
-
12140
- const specs = tx.Memos[0].data.split(';');
12135
+ tx.Memos[0].type === MemoTypes.HOST_UPDATE_INFO && tx.Memos[0].format === MemoFormats.HEX && tx.Memos[0].data) {
12141
12136
 
12142
12137
  return {
12143
12138
  name: EvernodeEvents.HostRegUpdated,
12144
12139
  data: {
12145
12140
  transaction: tx,
12146
- host: tx.Account,
12147
- version: specs[specs.length - 1],
12148
- specs: specs,
12141
+ host: tx.Account
12149
12142
  }
12150
12143
  }
12151
12144
  }
@@ -12238,13 +12231,16 @@ class BaseEvernodeClient {
12238
12231
  async getHosts(filters = null, pageSize = null, nextPageToken = null) {
12239
12232
  const hosts = await this.#firestoreHandler.getHosts(filters, pageSize, nextPageToken);
12240
12233
  const curMomentStartIdx = await this.getMomentStartIndex();
12241
- // Populate the host active status.
12242
- (hosts.nextPageToken ? hosts.data : hosts).forEach(h => {
12243
- h.active = (h.lastHeartbeatIndex > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
12244
- (h.lastHeartbeatIndex >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
12245
- (h.lastHeartbeatIndex > 0))
12246
- });
12247
- return hosts;
12234
+
12235
+ return await Promise.all((hosts.nextPageToken ? hosts.data : hosts).map(async host => {
12236
+ const hostAcc = new XrplAccount(host.address);
12237
+ host.domain = await hostAcc.getDomain();
12238
+
12239
+ host.active = (host.lastHeartbeatIndex > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
12240
+ (host.lastHeartbeatIndex >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
12241
+ (host.lastHeartbeatIndex > 0));
12242
+ return host;
12243
+ }));
12248
12244
  }
12249
12245
 
12250
12246
  /**
@@ -12304,11 +12300,28 @@ class BaseEvernodeClient {
12304
12300
  let memoData = Buffer.allocUnsafe(20);
12305
12301
  codec.decodeAccountID(hostAddress).copy(memoData);
12306
12302
 
12307
- await this.xrplAcc.makePayment(this.registryAddress,
12308
- XrplConstants.MIN_XRP_AMOUNT,
12309
- XrplConstants.XRP,
12310
- null,
12311
- [{ type: MemoTypes.DEAD_HOST_PRUNE, format: MemoFormats.HEX, data: memoData.toString('hex') }]);
12303
+ // To obtain registration NFT Page Keylet and index.
12304
+ const hostAcc = new XrplAccount(hostAddress, null, { xrplApi: this.xrplApi });
12305
+ const regNFT = (await hostAcc.getNfts()).find(n => n.URI.startsWith(EvernodeConstants.NFT_PREFIX_HEX) && n.Issuer === this.registryAddress);
12306
+ if (regNFT) {
12307
+ // Check whether the token was actually issued from Evernode registry contract.
12308
+ const issuerHex = regNFT.NFTokenID.substr(8, 40);
12309
+ const issuerAddr = codec.encodeAccountID(Buffer.from(issuerHex, 'hex'));
12310
+ if (issuerAddr == this.registryAddress) {
12311
+ const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, hostAcc, this.xrplApi);
12312
+
12313
+ await this.xrplAcc.makePayment(this.registryAddress,
12314
+ XrplConstants.MIN_XRP_AMOUNT,
12315
+ XrplConstants.XRP,
12316
+ null,
12317
+ [
12318
+ { type: MemoTypes.DEAD_HOST_PRUNE, format: MemoFormats.HEX, data: memoData.toString('hex') },
12319
+ { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
12320
+ ]);
12321
+ } else
12322
+ throw "Invalid Registration NFT."
12323
+ } else
12324
+ throw "No Registration NFT was found for the Host account."
12312
12325
 
12313
12326
  }
12314
12327
  }
@@ -12341,6 +12354,29 @@ const HostEvents = {
12341
12354
  ExtendLease: EvernodeEvents.ExtendLease
12342
12355
  }
12343
12356
 
12357
+ const HOST_COUNTRY_CODE_MEMO_OFFSET = 0;
12358
+ const HOST_CPU_MICROSEC_MEMO_OFFSET = 2;
12359
+ const HOST_RAM_MB_MEMO_OFFSET = 6;
12360
+ const HOST_DISK_MB_MEMO_OFFSET = 10;
12361
+ const HOST_TOT_INS_COUNT_MEMO_OFFSET = 14;
12362
+ const HOST_CPU_MODEL_NAME_MEMO_OFFSET = 18;
12363
+ const HOST_CPU_COUNT_MEMO_OFFSET = 58;
12364
+ const HOST_CPU_SPEED_MEMO_OFFSET = 60;
12365
+ const HOST_DESCRIPTION_MEMO_OFFSET = 62;
12366
+ const HOST_EMAIL_ADDRESS_MEMO_OFFSET = 88;
12367
+ const HOST_REG_MEMO_SIZE = 128;
12368
+
12369
+ const HOST_UPDATE_TOKEN_ID_MEMO_OFFSET = 0;
12370
+ const HOST_UPDATE_COUNTRY_CODE_MEMO_OFFSET = 32;
12371
+ const HOST_UPDATE_CPU_MICROSEC_MEMO_OFFSET = 34;
12372
+ const HOST_UPDATE_RAM_MB_MEMO_OFFSET = 38;
12373
+ const HOST_UPDATE_DISK_MB_MEMO_OFFSET = 42;
12374
+ const HOST_UPDATE_TOT_INS_COUNT_MEMO_OFFSET = 46;
12375
+ const HOST_UPDATE_ACT_INS_COUNT_MEMO_OFFSET = 50;
12376
+ const HOST_UPDATE_DESCRIPTION_MEMO_OFFSET = 54;
12377
+ const HOST_UPDATE_VERSION_MEMO_OFFSET = 80;
12378
+ const HOST_UPDATE_MEMO_SIZE = 83;
12379
+
12344
12380
  class HostClient extends BaseEvernodeClient {
12345
12381
 
12346
12382
  constructor(xrpAddress, xrpSecret, options = {}) {
@@ -12460,8 +12496,8 @@ class HostClient extends BaseEvernodeClient {
12460
12496
  throw "description should consist of 0-26 ascii characters except ';'";
12461
12497
 
12462
12498
  else if (!emailAddress || !(/[a-z0-9]+@[a-z]+.[a-z]{2,3}/.test(emailAddress)) || (emailAddress.length > 40))
12463
- throw "Email address should be valid and can not have more than 40 characters.";
12464
-
12499
+ throw "Email address should be valid and can not have more than 40 characters.";
12500
+
12465
12501
  if (await this.isRegistered())
12466
12502
  throw "Host already registered.";
12467
12503
 
@@ -12501,12 +12537,24 @@ class HostClient extends BaseEvernodeClient {
12501
12537
  console.log("No initiated transfers were found.");
12502
12538
  }
12503
12539
 
12504
- const memoData = `${countryCode};${cpuMicroSec};${ramMb};${diskMb};${totalInstanceCount};${cpuModel};${cpuCount};${cpuSpeed};${description};${emailAddress}`
12540
+ // <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)>
12541
+ const memoBuf = Buffer.alloc(HOST_REG_MEMO_SIZE, 0);
12542
+ Buffer.from(countryCode.substr(0, 2), "utf-8").copy(memoBuf, HOST_COUNTRY_CODE_MEMO_OFFSET);
12543
+ memoBuf.writeUInt32BE(cpuMicroSec, HOST_CPU_MICROSEC_MEMO_OFFSET);
12544
+ memoBuf.writeUInt32BE(ramMb, HOST_RAM_MB_MEMO_OFFSET);
12545
+ memoBuf.writeUInt32BE(diskMb, HOST_DISK_MB_MEMO_OFFSET);
12546
+ memoBuf.writeUInt32BE(totalInstanceCount, HOST_TOT_INS_COUNT_MEMO_OFFSET);
12547
+ Buffer.from(cpuModel.substr(0, 40), "utf-8").copy(memoBuf, HOST_CPU_MODEL_NAME_MEMO_OFFSET);
12548
+ memoBuf.writeUInt16BE(cpuCount, HOST_CPU_COUNT_MEMO_OFFSET);
12549
+ memoBuf.writeUInt16BE(cpuSpeed, HOST_CPU_SPEED_MEMO_OFFSET);
12550
+ Buffer.from(description.substr(0, 26), "utf-8").copy(memoBuf, HOST_DESCRIPTION_MEMO_OFFSET);
12551
+ Buffer.from(emailAddress.substr(0, 40), "utf-8").copy(memoBuf, HOST_EMAIL_ADDRESS_MEMO_OFFSET);
12552
+
12505
12553
  const tx = await this.xrplAcc.makePayment(this.registryAddress,
12506
12554
  (transferredNFTokenId) ? EvernodeConstants.NOW_IN_EVRS : this.config.hostRegFee.toString(),
12507
12555
  EvernodeConstants.EVR,
12508
12556
  this.config.evrIssuerAddress,
12509
- [{ type: MemoTypes.HOST_REG, format: MemoFormats.TEXT, data: memoData }],
12557
+ [{ type: MemoTypes.HOST_REG, format: MemoFormats.HEX, data: memoBuf.toString('hex') }],
12510
12558
  options.transactionOptions);
12511
12559
 
12512
12560
  console.log('Waiting for the sell offer')
@@ -12551,11 +12599,18 @@ class HostClient extends BaseEvernodeClient {
12551
12599
  throw "Host not registered."
12552
12600
 
12553
12601
  const regNFT = await this.getRegistrationNft();
12602
+
12603
+ // To obtain registration NFT Page Keylet and index.
12604
+ const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, this.xrplAcc, this.xrplApi);
12605
+
12554
12606
  await this.xrplAcc.makePayment(this.registryAddress,
12555
12607
  XrplConstants.MIN_XRP_AMOUNT,
12556
12608
  XrplConstants.XRP,
12557
12609
  null,
12558
- [{ type: MemoTypes.HOST_DEREG, format: MemoFormats.HEX, data: regNFT.NFTokenID }],
12610
+ [
12611
+ { type: MemoTypes.HOST_DEREG, format: MemoFormats.HEX, data: regNFT.NFTokenID },
12612
+ { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
12613
+ ],
12559
12614
  options.transactionOptions);
12560
12615
 
12561
12616
  console.log('Waiting for the buy offer')
@@ -12594,21 +12649,62 @@ class HostClient extends BaseEvernodeClient {
12594
12649
  }
12595
12650
 
12596
12651
  async updateRegInfo(activeInstanceCount = null, version = null, totalInstanceCount = null, tokenID = null, countryCode = null, cpuMicroSec = null, ramMb = null, diskMb = null, description = null, options = {}) {
12597
- const dataStr = `${tokenID ? tokenID : ''};${countryCode ? countryCode : ''};${cpuMicroSec ? cpuMicroSec : ''};${ramMb ? ramMb : ''};${diskMb ? diskMb : ''};${totalInstanceCount ? totalInstanceCount : ''};${activeInstanceCount !== undefined ? activeInstanceCount : ''};${description ? description : ''};${version ? version : ''}`;
12652
+ // <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)>
12653
+ const memoBuf = Buffer.alloc(HOST_UPDATE_MEMO_SIZE, 0);
12654
+ if (tokenID)
12655
+ Buffer.from(tokenID.substr(0, 32), "hex").copy(memoBuf, HOST_UPDATE_TOKEN_ID_MEMO_OFFSET);
12656
+ if (countryCode)
12657
+ Buffer.from(countryCode.substr(0, 2), "utf-8").copy(memoBuf, HOST_UPDATE_COUNTRY_CODE_MEMO_OFFSET);
12658
+ if (cpuMicroSec)
12659
+ memoBuf.writeUInt32BE(cpuMicroSec, HOST_UPDATE_CPU_MICROSEC_MEMO_OFFSET);
12660
+ if (ramMb)
12661
+ memoBuf.writeUInt32BE(ramMb, HOST_UPDATE_RAM_MB_MEMO_OFFSET);
12662
+ if (diskMb)
12663
+ memoBuf.writeUInt32BE(diskMb, HOST_UPDATE_DISK_MB_MEMO_OFFSET);
12664
+ if (totalInstanceCount)
12665
+ memoBuf.writeUInt32BE(totalInstanceCount, HOST_UPDATE_TOT_INS_COUNT_MEMO_OFFSET);
12666
+ if (activeInstanceCount)
12667
+ memoBuf.writeUInt32BE(activeInstanceCount, HOST_UPDATE_ACT_INS_COUNT_MEMO_OFFSET);
12668
+ if (description)
12669
+ Buffer.from(description.substr(0, 26), "utf-8").copy(memoBuf, HOST_UPDATE_DESCRIPTION_MEMO_OFFSET);
12670
+ if (version) {
12671
+ const components = version.split('.').map(v => parseInt(v));
12672
+ if (components.length != 3)
12673
+ throw 'Invalid version format.';
12674
+ memoBuf.writeUInt8(components[0], HOST_UPDATE_VERSION_MEMO_OFFSET);
12675
+ memoBuf.writeUInt8(components[1], HOST_UPDATE_VERSION_MEMO_OFFSET + 1);
12676
+ memoBuf.writeUInt8(components[2], HOST_UPDATE_VERSION_MEMO_OFFSET + 2);
12677
+ }
12678
+
12679
+ // To obtain registration NFT Page Keylet and index.
12680
+ if (!tokenID)
12681
+ tokenID = (await this.getRegistrationNft()).NFTokenID;
12682
+ const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(tokenID, this.xrplAcc, this.xrplApi);
12683
+
12598
12684
  return await this.xrplAcc.makePayment(this.registryAddress,
12599
12685
  XrplConstants.MIN_XRP_AMOUNT,
12600
12686
  XrplConstants.XRP,
12601
12687
  null,
12602
- [{ type: MemoTypes.HOST_UPDATE_INFO, format: MemoFormats.TEXT, data: dataStr }],
12688
+ [
12689
+ { type: MemoTypes.HOST_UPDATE_INFO, format: MemoFormats.HEX, data: memoBuf.toString('hex') },
12690
+ { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
12691
+ ],
12603
12692
  options.transactionOptions);
12604
12693
  }
12605
12694
 
12606
12695
  async heartbeat(options = {}) {
12696
+ // To obtain registration NFT Page Keylet and index.
12697
+ const regNFT = await this.getRegistrationNft();
12698
+ const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, this.xrplAcc, this.xrplApi);
12699
+
12607
12700
  return this.xrplAcc.makePayment(this.registryAddress,
12608
12701
  XrplConstants.MIN_XRP_AMOUNT,
12609
12702
  XrplConstants.XRP,
12610
12703
  null,
12611
- [{ type: MemoTypes.HEARTBEAT, format: "", data: "" }],
12704
+ [
12705
+ { type: MemoTypes.HEARTBEAT, format: "", data: "" },
12706
+ { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
12707
+ ],
12612
12708
  options.transactionOptions);
12613
12709
  }
12614
12710
 
@@ -12692,11 +12788,19 @@ class HostClient extends BaseEvernodeClient {
12692
12788
  }
12693
12789
 
12694
12790
  async requestRebate(options = {}) {
12791
+
12792
+ // To obtain registration NFT Page Keylet and index.
12793
+ const regNFT = await this.getRegistrationNft();
12794
+ const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, this.xrplAcc, this.xrplApi);
12795
+
12695
12796
  return this.xrplAcc.makePayment(this.registryAddress,
12696
12797
  XrplConstants.MIN_XRP_AMOUNT,
12697
12798
  XrplConstants.XRP,
12698
12799
  null,
12699
- [{ type: MemoTypes.HOST_REBATE, format: "", data: "" }],
12800
+ [
12801
+ { type: MemoTypes.HOST_REBATE, format: "", data: "" },
12802
+ { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
12803
+ ],
12700
12804
  options.transactionOptions);
12701
12805
  }
12702
12806
 
@@ -12727,16 +12831,21 @@ class HostClient extends BaseEvernodeClient {
12727
12831
  }
12728
12832
  }
12729
12833
 
12730
- const regNFT = (await this.xrplAcc.getNfts()).find(n => n.URI.startsWith(EvernodeConstants.NFT_PREFIX_HEX) && n.Issuer === this.registryAddress);
12731
-
12732
12834
  let memoData = Buffer.allocUnsafe(20);
12733
12835
  codec.decodeAccountID(transfereeAddress).copy(memoData);
12734
12836
 
12837
+ // To obtain registration NFT Page Keylet and index.
12838
+ const regNFT = await this.getRegistrationNft();
12839
+ const nftPageDataBuf = await EvernodeHelpers.getNFTPageAndLocation(regNFT.NFTokenID, this.xrplAcc, this.xrplApi);
12840
+
12735
12841
  await this.xrplAcc.makePayment(this.registryAddress,
12736
12842
  XrplConstants.MIN_XRP_AMOUNT,
12737
12843
  XrplConstants.XRP,
12738
12844
  null,
12739
- [{ type: MemoTypes.HOST_TRANSFER, format: MemoFormats.HEX, data: memoData.toString('hex') }],
12845
+ [
12846
+ { type: MemoTypes.HOST_TRANSFER, format: MemoFormats.HEX, data: memoData.toString('hex') },
12847
+ { type: MemoTypes.HOST_REGISTRY_REF, format: MemoFormats.HEX, data: nftPageDataBuf.toString('hex') }
12848
+ ],
12740
12849
  options.transactionOptions);
12741
12850
 
12742
12851
  let offer = null;
@@ -12768,6 +12877,19 @@ class HostClient extends BaseEvernodeClient {
12768
12877
 
12769
12878
  await this.xrplAcc.sellNft(offer.index);
12770
12879
  }
12880
+
12881
+ async hasPendingTransfer() {
12882
+
12883
+ // Check the availability of TRANSFEREE state for this host address.
12884
+ const stateTransfereeAddrKey = StateHelpers.generateTransfereeAddrStateKey(this.xrplAcc.address);
12885
+ const stateTransfereeAddrIndex = StateHelpers.getHookStateIndex(this.registryAddress, stateTransfereeAddrKey);
12886
+ const res = await this.xrplApi.getLedgerEntry(stateTransfereeAddrIndex);
12887
+
12888
+ if (res && res?.HookStateData)
12889
+ return true;
12890
+
12891
+ return false;
12892
+ }
12771
12893
  }
12772
12894
 
12773
12895
  module.exports = {
@@ -13594,7 +13716,8 @@ const MemoTypes = {
13594
13716
  REFUND: 'evnRefund',
13595
13717
  REFUND_REF: 'evnRefundRef',
13596
13718
  DEAD_HOST_PRUNE: 'evnDeadHostPrune',
13597
- HOST_REBATE: 'evnHostRebate'
13719
+ HOST_REBATE: 'evnHostRebate',
13720
+ HOST_REGISTRY_REF: 'evnHostRegistryRef'
13598
13721
  }
13599
13722
 
13600
13723
  const MemoFormats = {
@@ -13686,6 +13809,7 @@ module.exports = {
13686
13809
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
13687
13810
 
13688
13811
  const { EvernodeConstants } = __nccwpck_require__(9849);
13812
+ const NFT_PAGE_LEDGER_ENTRY_TYPE_HEX = '0050';
13689
13813
 
13690
13814
  class EvernodeHelpers {
13691
13815
  static async getLeaseOffers(xrplAcc) {
@@ -13694,6 +13818,36 @@ class EvernodeHelpers {
13694
13818
  const nftOffers = (await xrplAcc.getNftOffers())?.filter(offer => (offer.Flags == 1 && hostTokenIDs.includes(offer.NFTokenID))); // Filter only sell offers
13695
13819
  return nftOffers;
13696
13820
  }
13821
+
13822
+ static async getNFTPageAndLocation(nfTokenId, xrplAcc, xrplApi, buffer = true) {
13823
+
13824
+ const nftPageApprxKeylet = xrplAcc.generateKeylet('nftPage', { nfTokenId: nfTokenId });
13825
+ const nftPageMaxKeylet = xrplAcc.generateKeylet('nftPageMax');
13826
+ // Index is the last 32 bytes of the Keylet (Last 64 HEX characters).
13827
+ let page = await xrplApi.getLedgerEntry(nftPageMaxKeylet.substring(4, 68));
13828
+ while (page?.PreviousPageMin) {
13829
+ // Compare the low 96 bits. (Last 24 HEX characters).
13830
+ if (Number('0x' + page.index.substring(40, 64)) >= Number('0x' + nftPageApprxKeylet.substring(40, 64))) {
13831
+ // Check the existence of the NFToken
13832
+ let token = page.NFTokens.find(n => n.NFToken.NFTokenID == nfTokenId);
13833
+ if (!token) {
13834
+ page = await xrplApi.getLedgerEntry(page.PreviousPageMin);
13835
+ }
13836
+ else
13837
+ break;
13838
+ }
13839
+ }
13840
+
13841
+ const nftPageInfo = page.NFTokens.map((n, loc) => { return { NFTPage: NFT_PAGE_LEDGER_ENTRY_TYPE_HEX + page.index, NFTokenID: n.NFToken.NFTokenID, location: loc } }).find(n => n.NFTokenID == nfTokenId);
13842
+ if (buffer) {
13843
+ let locBuf = Buffer.allocUnsafe(2);
13844
+ locBuf.writeUInt16BE(nftPageInfo.location);
13845
+ // <NFT_PAGE_KEYLET(34 bytes)><LOCATION(2 bytes)>
13846
+ return Buffer.concat([Buffer.from(nftPageInfo.NFTPage, "hex"), locBuf]);
13847
+ }
13848
+
13849
+ return nftPageInfo;
13850
+ }
13697
13851
  }
13698
13852
 
13699
13853
  module.exports = {
@@ -14110,7 +14264,7 @@ const HOST_CPU_SPEED_OFFSET = 62;
14110
14264
  const HOST_CPU_MICROSEC_OFFSET = 64;
14111
14265
  const HOST_RAM_MB_OFFSET = 68;
14112
14266
  const HOST_DISK_MB_OFFSET = 72;
14113
- const EMAIL_ADDRESS_OFFSET = 112;
14267
+ const HOST_EMAIL_ADDRESS_OFFSET = 76;
14114
14268
 
14115
14269
  const PREV_HOST_ADDRESS_OFFSET = 0;
14116
14270
  const TRANSFER_LEDGER_IDX_OFFSET = 20;
@@ -14137,6 +14291,7 @@ const HOST_ADDR_KEY_ZERO_COUNT = 8;
14137
14291
  const TRANSFEREE_ADDR_KEY_ZERO_COUNT = 8;
14138
14292
  const HOOK_STATE_LEDGER_TYPE_PREFIX = 118; // Decimal value of ASCII 'v'
14139
14293
  const PENDING_TRANSFER = 1;
14294
+ const HOST_EMAIL_ADDRESS_LEN = 40;
14140
14295
 
14141
14296
  class StateHelpers {
14142
14297
  static StateTypes = {
@@ -14187,7 +14342,9 @@ class StateHelpers {
14187
14342
  cpuMicrosec: stateDataBuf.readUInt32BE(HOST_CPU_MICROSEC_OFFSET),
14188
14343
  ramMb: stateDataBuf.readUInt32BE(HOST_RAM_MB_OFFSET),
14189
14344
  diskMb: stateDataBuf.readUInt32BE(HOST_DISK_MB_OFFSET),
14190
- email: (stateDataBuf.length > HOST_DISK_MB_OFFSET ? stateDataBuf.slice(HOST_DISK_MB_OFFSET, EMAIL_ADDRESS_OFFSET).toString() : "")
14345
+ email: (stateDataBuf.length > HOST_EMAIL_ADDRESS_OFFSET ?
14346
+ stateDataBuf.slice(HOST_EMAIL_ADDRESS_OFFSET, HOST_EMAIL_ADDRESS_OFFSET + HOST_EMAIL_ADDRESS_LEN).toString().toString().replace(/\0/g, '') :
14347
+ "")
14191
14348
  }
14192
14349
  }
14193
14350
 
@@ -14738,18 +14895,27 @@ class XrplAccount {
14738
14895
  #subscribed = false;
14739
14896
  #txStreamHandler;
14740
14897
 
14741
- constructor(address, secret = null, options = {}) {
14898
+ constructor(address = null, secret = null, options = {}) {
14899
+ if (!address && !secret)
14900
+ throw "Both address and secret cannot be empty";
14901
+
14902
+ this.address = address;
14903
+ this.secret = secret;
14742
14904
  this.xrplApi = options.xrplApi || DefaultValues.xrplApi;
14743
14905
 
14744
14906
  if (!this.xrplApi)
14745
14907
  throw "XrplAccount: xrplApi not specified.";
14746
14908
 
14747
- this.address = address;
14748
-
14749
- this.secret = secret;
14750
- if (this.secret) {
14909
+ if (!this.address && this.secret) {
14751
14910
  this.wallet = xrpl.Wallet.fromSeed(this.secret);
14752
- this.address= this.wallet.classicAddress;
14911
+ this.address = this.wallet.classicAddress;
14912
+ } else if (this.secret) {
14913
+ const keypair = kp.deriveKeypair(this.secret);
14914
+ const derivedPubKeyAddress = kp.deriveAddress(keypair.publicKey);
14915
+ if (this.address == derivedPubKeyAddress)
14916
+ this.wallet = xrpl.Wallet.fromSeed(this.secret);
14917
+ else
14918
+ this.wallet = xrpl.Wallet.fromSeed(this.secret, { masterAddress: this.address });
14753
14919
  }
14754
14920
 
14755
14921
  this.#txStreamHandler = (eventName, tx, error) => {
@@ -14776,6 +14942,10 @@ class XrplAccount {
14776
14942
  return kp.deriveKeypair(this.secret);
14777
14943
  }
14778
14944
 
14945
+ async exists() {
14946
+ return await this.xrplApi.isAccountExists(this.address);
14947
+ }
14948
+
14779
14949
  async getInfo() {
14780
14950
  return await this.xrplApi.getAccountInfo(this.address);
14781
14951
  }
@@ -14849,6 +15019,10 @@ class XrplAccount {
14849
15019
  return await this.xrplApi.getAccountTrx(this.address, { ledger_index_min: minLedgerIndex, ledger_index_max: maxLedgerIndex, forward: isForward });
14850
15020
  }
14851
15021
 
15022
+ async hasValidKeyPair() {
15023
+ return await this.xrplApi.isValidKeyForAddress(this.wallet.publicKey, this.address);
15024
+ }
15025
+
14852
15026
  setAccountFields(fields, options = {}) {
14853
15027
  /**
14854
15028
  * Example for fields
@@ -15072,6 +15246,29 @@ class XrplAccount {
15072
15246
  return this.#submitAndVerifyTransaction(owner ? { ...tx, Owner: owner } : tx, options);
15073
15247
  }
15074
15248
 
15249
+ generateKeylet(type, data = {}) {
15250
+ switch (type) {
15251
+ case 'nftPage': {
15252
+ const accIdHex = (codec.decodeAccountID(this.address)).toString('hex').toUpperCase();
15253
+ const tokenPortion = data?.nfTokenId.substr(40, 64);
15254
+ return '0050' + accIdHex + tokenPortion;
15255
+ }
15256
+
15257
+ case 'nftPageMax': {
15258
+ const accIdHex = (codec.decodeAccountID(this.address)).toString('hex').toUpperCase();
15259
+ return '0050' + accIdHex + 'F'.repeat(24);
15260
+ }
15261
+
15262
+ case 'nftPageMin': {
15263
+ const accIdHex = (codec.decodeAccountID(this.address)).toString('hex').toUpperCase();
15264
+ return '0050' + accIdHex + '0'.repeat(24);
15265
+ }
15266
+
15267
+ default:
15268
+ return null;
15269
+ }
15270
+ }
15271
+
15075
15272
  async subscribe() {
15076
15273
  // Subscribe only once. Otherwise event handlers will be duplicated.
15077
15274
  if (this.#subscribed)
@@ -15407,6 +15604,17 @@ class XrplApi {
15407
15604
  return derivedPubKeyAddress === address || (regularKey && derivedPubKeyAddress === regularKey);
15408
15605
  }
15409
15606
 
15607
+ async isAccountExists(address) {
15608
+ try {
15609
+ await this.#client.request({ command: 'account_info', account: address });
15610
+ return true;
15611
+ }
15612
+ catch (e) {
15613
+ if (e.data.error === 'actNotFound') return false;
15614
+ else throw e;
15615
+ }
15616
+ }
15617
+
15410
15618
  async getAccountInfo(address) {
15411
15619
  const resp = (await this.#client.request({ command: 'account_info', account: address }));
15412
15620
  return resp?.result?.account_data;
@@ -15451,8 +15659,15 @@ class XrplApi {
15451
15659
  }
15452
15660
 
15453
15661
  async getLedgerEntry(index, options) {
15454
- const resp = (await this.#client.request({ command: 'ledger_entry', index: index, ledger_index: "validated", ...options }));
15455
- return resp?.result?.node;
15662
+ try {
15663
+ const resp = (await this.#client.request({ command: 'ledger_entry', index: index, ledger_index: "validated", ...options }));
15664
+ return resp?.result?.node;
15665
+
15666
+ } catch (e) {
15667
+ if (e?.data?.error === 'entryNotFound')
15668
+ return null;
15669
+ throw e;
15670
+ }
15456
15671
  }
15457
15672
 
15458
15673
  async submitAndVerify(tx, options) {
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  ],
7
7
  "homepage": "https://github.com/HotPocketDev/evernode-js-client",
8
8
  "license": "MIT",
9
- "version": "0.5.9",
9
+ "version": "0.5.10",
10
10
  "dependencies": {
11
11
  "elliptic": "6.5.4",
12
12
  "libsodium-wrappers": "0.7.10",