evernode-js-client 0.5.8 → 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 +281 -49
  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
 
@@ -11905,7 +11906,8 @@ class BaseEvernodeClient {
11905
11906
  rewardInfo: HookStateKeys.REWARD_INFO,
11906
11907
  rewardConfiguration: HookStateKeys.REWARD_CONFIGURATION,
11907
11908
  hostCount: HookStateKeys.HOST_COUNT,
11908
- momentTransitInfo: HookStateKeys.MOMENT_TRANSIT_INFO
11909
+ momentTransitInfo: HookStateKeys.MOMENT_TRANSIT_INFO,
11910
+ registryMaxTrxEmitFee: HookStateKeys.MAX_TRX_EMISSION_FEE
11909
11911
  }
11910
11912
  let config = {};
11911
11913
  for (const [key, value] of Object.entries(configStateKeys)) {
@@ -12036,17 +12038,13 @@ class BaseEvernodeClient {
12036
12038
  }
12037
12039
  }
12038
12040
  else if (tx.Memos.length >= 1 &&
12039
- 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) {
12040
12042
 
12041
- const parts = tx.Memos[0].data.split(';');
12042
12043
  return {
12043
12044
  name: EvernodeEvents.HostRegistered,
12044
12045
  data: {
12045
12046
  transaction: tx,
12046
- host: tx.Account,
12047
- token: parts[0],
12048
- instanceSize: parts[1],
12049
- location: parts[2]
12047
+ host: tx.Account
12050
12048
  }
12051
12049
  }
12052
12050
  }
@@ -12134,17 +12132,13 @@ class BaseEvernodeClient {
12134
12132
  }
12135
12133
  }
12136
12134
  else if (tx.Memos.length >= 1 &&
12137
- tx.Memos[0].type === MemoTypes.HOST_UPDATE_INFO && tx.Memos[0].format === MemoFormats.TEXT && tx.Memos[0].data) {
12138
-
12139
- 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) {
12140
12136
 
12141
12137
  return {
12142
12138
  name: EvernodeEvents.HostRegUpdated,
12143
12139
  data: {
12144
12140
  transaction: tx,
12145
- host: tx.Account,
12146
- version: specs[specs.length - 1],
12147
- specs: specs,
12141
+ host: tx.Account
12148
12142
  }
12149
12143
  }
12150
12144
  }
@@ -12237,13 +12231,16 @@ class BaseEvernodeClient {
12237
12231
  async getHosts(filters = null, pageSize = null, nextPageToken = null) {
12238
12232
  const hosts = await this.#firestoreHandler.getHosts(filters, pageSize, nextPageToken);
12239
12233
  const curMomentStartIdx = await this.getMomentStartIndex();
12240
- // Populate the host active status.
12241
- (hosts.nextPageToken ? hosts.data : hosts).forEach(h => {
12242
- h.active = (h.lastHeartbeatIndex > (this.config.hostHeartbeatFreq * this.config.momentSize) ?
12243
- (h.lastHeartbeatIndex >= (curMomentStartIdx - (this.config.hostHeartbeatFreq * this.config.momentSize))) :
12244
- (h.lastHeartbeatIndex > 0))
12245
- });
12246
- 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
+ }));
12247
12244
  }
12248
12245
 
12249
12246
  /**
@@ -12303,11 +12300,28 @@ class BaseEvernodeClient {
12303
12300
  let memoData = Buffer.allocUnsafe(20);
12304
12301
  codec.decodeAccountID(hostAddress).copy(memoData);
12305
12302
 
12306
- await this.xrplAcc.makePayment(this.registryAddress,
12307
- XrplConstants.MIN_XRP_AMOUNT,
12308
- XrplConstants.XRP,
12309
- null,
12310
- [{ 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."
12311
12325
 
12312
12326
  }
12313
12327
  }
@@ -12340,6 +12354,29 @@ const HostEvents = {
12340
12354
  ExtendLease: EvernodeEvents.ExtendLease
12341
12355
  }
12342
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
+
12343
12380
  class HostClient extends BaseEvernodeClient {
12344
12381
 
12345
12382
  constructor(xrpAddress, xrpSecret, options = {}) {
@@ -12459,8 +12496,8 @@ class HostClient extends BaseEvernodeClient {
12459
12496
  throw "description should consist of 0-26 ascii characters except ';'";
12460
12497
 
12461
12498
  else if (!emailAddress || !(/[a-z0-9]+@[a-z]+.[a-z]{2,3}/.test(emailAddress)) || (emailAddress.length > 40))
12462
- throw "Email address should be valid and can not have more than 40 characters.";
12463
-
12499
+ throw "Email address should be valid and can not have more than 40 characters.";
12500
+
12464
12501
  if (await this.isRegistered())
12465
12502
  throw "Host already registered.";
12466
12503
 
@@ -12500,12 +12537,24 @@ class HostClient extends BaseEvernodeClient {
12500
12537
  console.log("No initiated transfers were found.");
12501
12538
  }
12502
12539
 
12503
- 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
+
12504
12553
  const tx = await this.xrplAcc.makePayment(this.registryAddress,
12505
12554
  (transferredNFTokenId) ? EvernodeConstants.NOW_IN_EVRS : this.config.hostRegFee.toString(),
12506
12555
  EvernodeConstants.EVR,
12507
12556
  this.config.evrIssuerAddress,
12508
- [{ type: MemoTypes.HOST_REG, format: MemoFormats.TEXT, data: memoData }],
12557
+ [{ type: MemoTypes.HOST_REG, format: MemoFormats.HEX, data: memoBuf.toString('hex') }],
12509
12558
  options.transactionOptions);
12510
12559
 
12511
12560
  console.log('Waiting for the sell offer')
@@ -12550,11 +12599,18 @@ class HostClient extends BaseEvernodeClient {
12550
12599
  throw "Host not registered."
12551
12600
 
12552
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
+
12553
12606
  await this.xrplAcc.makePayment(this.registryAddress,
12554
12607
  XrplConstants.MIN_XRP_AMOUNT,
12555
12608
  XrplConstants.XRP,
12556
12609
  null,
12557
- [{ 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
+ ],
12558
12614
  options.transactionOptions);
12559
12615
 
12560
12616
  console.log('Waiting for the buy offer')
@@ -12593,21 +12649,62 @@ class HostClient extends BaseEvernodeClient {
12593
12649
  }
12594
12650
 
12595
12651
  async updateRegInfo(activeInstanceCount = null, version = null, totalInstanceCount = null, tokenID = null, countryCode = null, cpuMicroSec = null, ramMb = null, diskMb = null, description = null, options = {}) {
12596
- 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
+
12597
12684
  return await this.xrplAcc.makePayment(this.registryAddress,
12598
12685
  XrplConstants.MIN_XRP_AMOUNT,
12599
12686
  XrplConstants.XRP,
12600
12687
  null,
12601
- [{ 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
+ ],
12602
12692
  options.transactionOptions);
12603
12693
  }
12604
12694
 
12605
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
+
12606
12700
  return this.xrplAcc.makePayment(this.registryAddress,
12607
12701
  XrplConstants.MIN_XRP_AMOUNT,
12608
12702
  XrplConstants.XRP,
12609
12703
  null,
12610
- [{ 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
+ ],
12611
12708
  options.transactionOptions);
12612
12709
  }
12613
12710
 
@@ -12691,11 +12788,19 @@ class HostClient extends BaseEvernodeClient {
12691
12788
  }
12692
12789
 
12693
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
+
12694
12796
  return this.xrplAcc.makePayment(this.registryAddress,
12695
12797
  XrplConstants.MIN_XRP_AMOUNT,
12696
12798
  XrplConstants.XRP,
12697
12799
  null,
12698
- [{ 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
+ ],
12699
12804
  options.transactionOptions);
12700
12805
  }
12701
12806
 
@@ -12726,16 +12831,21 @@ class HostClient extends BaseEvernodeClient {
12726
12831
  }
12727
12832
  }
12728
12833
 
12729
- const regNFT = (await this.xrplAcc.getNfts()).find(n => n.URI.startsWith(EvernodeConstants.NFT_PREFIX_HEX) && n.Issuer === this.registryAddress);
12730
-
12731
12834
  let memoData = Buffer.allocUnsafe(20);
12732
12835
  codec.decodeAccountID(transfereeAddress).copy(memoData);
12733
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
+
12734
12841
  await this.xrplAcc.makePayment(this.registryAddress,
12735
12842
  XrplConstants.MIN_XRP_AMOUNT,
12736
12843
  XrplConstants.XRP,
12737
12844
  null,
12738
- [{ 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
+ ],
12739
12849
  options.transactionOptions);
12740
12850
 
12741
12851
  let offer = null;
@@ -12767,6 +12877,19 @@ class HostClient extends BaseEvernodeClient {
12767
12877
 
12768
12878
  await this.xrplAcc.sellNft(offer.index);
12769
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
+ }
12770
12893
  }
12771
12894
 
12772
12895
  module.exports = {
@@ -13593,7 +13716,8 @@ const MemoTypes = {
13593
13716
  REFUND: 'evnRefund',
13594
13717
  REFUND_REF: 'evnRefundRef',
13595
13718
  DEAD_HOST_PRUNE: 'evnDeadHostPrune',
13596
- HOST_REBATE: 'evnHostRebate'
13719
+ HOST_REBATE: 'evnHostRebate',
13720
+ HOST_REGISTRY_REF: 'evnHostRegistryRef'
13597
13721
  }
13598
13722
 
13599
13723
  const MemoFormats = {
@@ -13634,6 +13758,8 @@ const HookStateKeys = {
13634
13758
  REWARD_CONFIGURATION: "4556520100000000000000000000000000000000000000000000000000000009",
13635
13759
  MAX_TOLERABLE_DOWNTIME: "455652010000000000000000000000000000000000000000000000000000000A",
13636
13760
  MOMENT_TRANSIT_INFO: "455652010000000000000000000000000000000000000000000000000000000B",
13761
+ MAX_TRX_EMISSION_FEE: "455652010000000000000000000000000000000000000000000000000000000C",
13762
+
13637
13763
 
13638
13764
  // Singleton
13639
13765
  HOST_COUNT: "4556523200000000000000000000000000000000000000000000000000000000",
@@ -13683,6 +13809,7 @@ module.exports = {
13683
13809
  /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
13684
13810
 
13685
13811
  const { EvernodeConstants } = __nccwpck_require__(9849);
13812
+ const NFT_PAGE_LEDGER_ENTRY_TYPE_HEX = '0050';
13686
13813
 
13687
13814
  class EvernodeHelpers {
13688
13815
  static async getLeaseOffers(xrplAcc) {
@@ -13691,6 +13818,36 @@ class EvernodeHelpers {
13691
13818
  const nftOffers = (await xrplAcc.getNftOffers())?.filter(offer => (offer.Flags == 1 && hostTokenIDs.includes(offer.NFTokenID))); // Filter only sell offers
13692
13819
  return nftOffers;
13693
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
+ }
13694
13851
  }
13695
13852
 
13696
13853
  module.exports = {
@@ -14107,7 +14264,7 @@ const HOST_CPU_SPEED_OFFSET = 62;
14107
14264
  const HOST_CPU_MICROSEC_OFFSET = 64;
14108
14265
  const HOST_RAM_MB_OFFSET = 68;
14109
14266
  const HOST_DISK_MB_OFFSET = 72;
14110
- const EMAIL_ADDRESS_OFFSET = 112;
14267
+ const HOST_EMAIL_ADDRESS_OFFSET = 76;
14111
14268
 
14112
14269
  const PREV_HOST_ADDRESS_OFFSET = 0;
14113
14270
  const TRANSFER_LEDGER_IDX_OFFSET = 20;
@@ -14134,6 +14291,7 @@ const HOST_ADDR_KEY_ZERO_COUNT = 8;
14134
14291
  const TRANSFEREE_ADDR_KEY_ZERO_COUNT = 8;
14135
14292
  const HOOK_STATE_LEDGER_TYPE_PREFIX = 118; // Decimal value of ASCII 'v'
14136
14293
  const PENDING_TRANSFER = 1;
14294
+ const HOST_EMAIL_ADDRESS_LEN = 40;
14137
14295
 
14138
14296
  class StateHelpers {
14139
14297
  static StateTypes = {
@@ -14184,7 +14342,9 @@ class StateHelpers {
14184
14342
  cpuMicrosec: stateDataBuf.readUInt32BE(HOST_CPU_MICROSEC_OFFSET),
14185
14343
  ramMb: stateDataBuf.readUInt32BE(HOST_RAM_MB_OFFSET),
14186
14344
  diskMb: stateDataBuf.readUInt32BE(HOST_DISK_MB_OFFSET),
14187
- 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
+ "")
14188
14348
  }
14189
14349
  }
14190
14350
 
@@ -14329,6 +14489,13 @@ class StateHelpers {
14329
14489
  }
14330
14490
  }
14331
14491
  }
14492
+ else if (Buffer.from(HookStateKeys.MAX_TRX_EMISSION_FEE, 'hex').compare(stateKey) === 0) {
14493
+ return {
14494
+ type: this.StateTypes.CONFIGURATION,
14495
+ key: hexKey,
14496
+ value: Number(stateData.readBigUInt64BE())
14497
+ }
14498
+ }
14332
14499
  else
14333
14500
  throw { type: 'Validation Error', message: 'Invalid state key.' };
14334
14501
  }
@@ -14347,6 +14514,12 @@ class StateHelpers {
14347
14514
  type: this.StateTypes.TOKEN_ID
14348
14515
  };
14349
14516
  }
14517
+ else if (Buffer.from(HookStateKeys.PREFIX_TRANSFEREE_ADDR, 'hex').compare(stateKey, 0, 4) === 0) {
14518
+ return {
14519
+ key: hexKey,
14520
+ type: this.StateTypes.TRANSFEREE_ADDR
14521
+ };
14522
+ }
14350
14523
  else if (Buffer.from(HookStateKeys.HOST_COUNT, 'hex').compare(stateKey) === 0 ||
14351
14524
  Buffer.from(HookStateKeys.MOMENT_BASE_INFO, 'hex').compare(stateKey) === 0 ||
14352
14525
  Buffer.from(HookStateKeys.HOST_REG_FEE, 'hex').compare(stateKey) === 0 ||
@@ -14367,7 +14540,8 @@ class StateHelpers {
14367
14540
  Buffer.from(HookStateKeys.LEASE_ACQUIRE_WINDOW, 'hex').compare(stateKey) === 0 ||
14368
14541
  Buffer.from(HookStateKeys.REWARD_CONFIGURATION, 'hex').compare(stateKey) === 0 ||
14369
14542
  Buffer.from(HookStateKeys.MAX_TOLERABLE_DOWNTIME, 'hex').compare(stateKey) === 0 ||
14370
- Buffer.from(HookStateKeys.MOMENT_TRANSIT_INFO, 'hex').compare(stateKey) === 0) {
14543
+ Buffer.from(HookStateKeys.MOMENT_TRANSIT_INFO, 'hex').compare(stateKey) === 0 ||
14544
+ Buffer.from(HookStateKeys.MAX_TRX_EMISSION_FEE, 'hex').compare(stateKey) === 0) {
14371
14545
  return {
14372
14546
  key: hexKey,
14373
14547
  type: this.STATE_TYPES.CONFIGURATION
@@ -14721,18 +14895,27 @@ class XrplAccount {
14721
14895
  #subscribed = false;
14722
14896
  #txStreamHandler;
14723
14897
 
14724
- 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;
14725
14904
  this.xrplApi = options.xrplApi || DefaultValues.xrplApi;
14726
14905
 
14727
14906
  if (!this.xrplApi)
14728
14907
  throw "XrplAccount: xrplApi not specified.";
14729
14908
 
14730
- this.address = address;
14731
-
14732
- this.secret = secret;
14733
- if (this.secret) {
14909
+ if (!this.address && this.secret) {
14734
14910
  this.wallet = xrpl.Wallet.fromSeed(this.secret);
14735
- 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 });
14736
14919
  }
14737
14920
 
14738
14921
  this.#txStreamHandler = (eventName, tx, error) => {
@@ -14759,6 +14942,10 @@ class XrplAccount {
14759
14942
  return kp.deriveKeypair(this.secret);
14760
14943
  }
14761
14944
 
14945
+ async exists() {
14946
+ return await this.xrplApi.isAccountExists(this.address);
14947
+ }
14948
+
14762
14949
  async getInfo() {
14763
14950
  return await this.xrplApi.getAccountInfo(this.address);
14764
14951
  }
@@ -14832,6 +15019,10 @@ class XrplAccount {
14832
15019
  return await this.xrplApi.getAccountTrx(this.address, { ledger_index_min: minLedgerIndex, ledger_index_max: maxLedgerIndex, forward: isForward });
14833
15020
  }
14834
15021
 
15022
+ async hasValidKeyPair() {
15023
+ return await this.xrplApi.isValidKeyForAddress(this.wallet.publicKey, this.address);
15024
+ }
15025
+
14835
15026
  setAccountFields(fields, options = {}) {
14836
15027
  /**
14837
15028
  * Example for fields
@@ -15055,6 +15246,29 @@ class XrplAccount {
15055
15246
  return this.#submitAndVerifyTransaction(owner ? { ...tx, Owner: owner } : tx, options);
15056
15247
  }
15057
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
+
15058
15272
  async subscribe() {
15059
15273
  // Subscribe only once. Otherwise event handlers will be duplicated.
15060
15274
  if (this.#subscribed)
@@ -15390,6 +15604,17 @@ class XrplApi {
15390
15604
  return derivedPubKeyAddress === address || (regularKey && derivedPubKeyAddress === regularKey);
15391
15605
  }
15392
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
+
15393
15618
  async getAccountInfo(address) {
15394
15619
  const resp = (await this.#client.request({ command: 'account_info', account: address }));
15395
15620
  return resp?.result?.account_data;
@@ -15434,8 +15659,15 @@ class XrplApi {
15434
15659
  }
15435
15660
 
15436
15661
  async getLedgerEntry(index, options) {
15437
- const resp = (await this.#client.request({ command: 'ledger_entry', index: index, ledger_index: "validated", ...options }));
15438
- 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
+ }
15439
15671
  }
15440
15672
 
15441
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.8",
9
+ "version": "0.5.10",
10
10
  "dependencies": {
11
11
  "elliptic": "6.5.4",
12
12
  "libsodium-wrappers": "0.7.10",