evernode-js-client 0.5.9 → 0.5.10

Sign up to get free protection for your applications and to get access to all the features.
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",