evernode-js-client 0.5.13 → 0.5.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,515 +0,0 @@
1
- const xrpl = require('xrpl');
2
- const kp = require('ripple-keypairs');
3
- const codec = require('ripple-address-codec');
4
- const crypto = require("crypto");
5
- const { XrplConstants } = require('./xrpl-common');
6
- const { TransactionHelper } = require('./transaction-helper');
7
- const { EventEmitter } = require('./event-emitter');
8
- const { DefaultValues } = require('./defaults');
9
- const xrplCodec = require('xrpl-binary-codec');
10
-
11
- class XrplAccount {
12
-
13
- #events = new EventEmitter();
14
- #subscribed = false;
15
- #txStreamHandler;
16
-
17
- constructor(address = null, secret = null, options = {}) {
18
- if (!address && !secret)
19
- throw "Both address and secret cannot be empty";
20
-
21
- this.address = address;
22
- this.secret = secret;
23
- this.xrplApi = options.xrplApi || DefaultValues.xrplApi;
24
-
25
- if (!this.xrplApi)
26
- throw "XrplAccount: xrplApi not specified.";
27
-
28
- if (!this.address && this.secret) {
29
- this.wallet = xrpl.Wallet.fromSeed(this.secret);
30
- this.address = this.wallet.classicAddress;
31
- } else if (this.secret) {
32
- const keypair = kp.deriveKeypair(this.secret);
33
- const derivedPubKeyAddress = kp.deriveAddress(keypair.publicKey);
34
- if (this.address == derivedPubKeyAddress)
35
- this.wallet = xrpl.Wallet.fromSeed(this.secret);
36
- else
37
- this.wallet = xrpl.Wallet.fromSeed(this.secret, { masterAddress: this.address });
38
- }
39
-
40
- this.#txStreamHandler = (eventName, tx, error) => {
41
- this.#events.emit(eventName, tx, error);
42
- };
43
- }
44
-
45
- on(event, handler) {
46
- this.#events.on(event, handler);
47
- }
48
-
49
- once(event, handler) {
50
- this.#events.once(event, handler);
51
- }
52
-
53
- off(event, handler = null) {
54
- this.#events.off(event, handler);
55
- }
56
-
57
- deriveKeypair() {
58
- if (!this.secret)
59
- throw 'Cannot derive key pair: Account secret is empty.';
60
-
61
- return kp.deriveKeypair(this.secret);
62
- }
63
-
64
- async exists() {
65
- return await this.xrplApi.isAccountExists(this.address);
66
- }
67
-
68
- async getInfo() {
69
- return await this.xrplApi.getAccountInfo(this.address);
70
- }
71
-
72
- async getSequence() {
73
- return (await this.getInfo())?.Sequence;
74
- }
75
-
76
- async getMintedNFTokens() {
77
- return ((await this.getInfo())?.MintedNFTokens || 0);
78
- }
79
-
80
- async getBurnedNFTokens() {
81
- return ((await this.getInfo())?.BurnedNFTokens || 0);
82
- }
83
-
84
- async getMessageKey() {
85
- return (await this.getInfo())?.MessageKey;
86
- }
87
-
88
- async getDomain() {
89
- const domain = (await this.getInfo())?.Domain;
90
- return domain ? TransactionHelper.hexToASCII(domain) : null;
91
- }
92
-
93
- async getTrustLines(currency, issuer) {
94
- const lines = await this.xrplApi.getTrustlines(this.address, {
95
- limit: 399,
96
- peer: issuer
97
- });
98
- return currency ? lines.filter(l => l.currency === currency) : lines;
99
- }
100
-
101
- async getChecks(fromAccount) {
102
- return await this.xrplApi.getAccountObjects(fromAccount, { type: "check" });
103
- }
104
-
105
- async getNfts() {
106
- return await this.xrplApi.getNfts(this.address, {
107
- limit: 399
108
- });
109
- }
110
-
111
- async getOffers() {
112
- return await this.xrplApi.getOffers(this.address);
113
- }
114
-
115
- async getNftOffers() {
116
- return await this.xrplApi.getNftOffers(this.address);
117
- }
118
-
119
- async getNftByUri(uri, isHexUri = false) {
120
- const nfts = await this.getNfts();
121
- const hexUri = isHexUri ? uri : TransactionHelper.asciiToHex(uri).toUpperCase();
122
- return nfts.find(n => n.URI == hexUri);
123
- }
124
-
125
- async getAccountObjects(options) {
126
- return await this.xrplApi.getAccountObjects(this.address, options);
127
- }
128
-
129
- async getNamespaceEntries(namespaceId, options = {}) {
130
- return await this.xrplApi.getNamespaceEntries(this.address, namespaceId, options);
131
- }
132
-
133
- async getFlags() {
134
- return xrpl.parseAccountRootFlags((await this.getInfo()).Flags);
135
- }
136
-
137
- async getAccountTrx(minLedgerIndex = -1, maxLedgerIndex = -1, isForward = true) {
138
- return await this.xrplApi.getAccountTrx(this.address, { ledger_index_min: minLedgerIndex, ledger_index_max: maxLedgerIndex, forward: isForward });
139
- }
140
-
141
- async hasValidKeyPair() {
142
- return await this.xrplApi.isValidKeyForAddress(this.wallet.publicKey, this.address);
143
- }
144
-
145
- setAccountFields(fields, options = {}) {
146
- /**
147
- * Example for fields
148
- *
149
- * fields = {
150
- * Domain : "www.mydomain.com",
151
- * Flags : { asfDefaultRipple: false, asfDisableMaster: true }
152
- * }
153
- *
154
- */
155
-
156
- if (Object.keys(fields).length === 0)
157
- throw "AccountSet fields cannot be empty.";
158
-
159
- const tx = {
160
- TransactionType: 'AccountSet',
161
- Account: this.address
162
- };
163
-
164
- for (const [key, value] of Object.entries(fields)) {
165
-
166
- switch (key) {
167
- case 'Domain':
168
- tx.Domain = TransactionHelper.asciiToHex(value).toUpperCase();
169
- break;
170
-
171
- case 'Flags':
172
- for (const [flagKey, flagValue] of Object.entries(value)) {
173
- tx[(flagValue) ? 'SetFlag' : 'ClearFlag'] |= xrpl.AccountSetAsfFlags[flagKey];
174
- }
175
- break;
176
-
177
- default:
178
- tx[key] = value;
179
- break;
180
- }
181
- }
182
-
183
- return this.#submitAndVerifyTransaction(tx, options);
184
- }
185
-
186
- makePayment(toAddr, amount, currency, issuer = null, memos = null, options = {}) {
187
-
188
- const amountObj = makeAmountObject(amount, currency, issuer);
189
-
190
- return this.#submitAndVerifyTransaction({
191
- TransactionType: 'Payment',
192
- Account: this.address,
193
- Amount: amountObj,
194
- Destination: toAddr,
195
- Memos: TransactionHelper.formatMemos(memos)
196
- }, options);
197
- }
198
-
199
- setTrustLine(currency, issuer, limit, allowRippling = false, memos = null, options = {}) {
200
-
201
- if (typeof limit !== 'string')
202
- throw "Limit must be a string.";
203
-
204
- let tx = {
205
- TransactionType: 'TrustSet',
206
- Account: this.address,
207
- LimitAmount: {
208
- currency: currency,
209
- issuer: issuer,
210
- value: limit
211
- },
212
- Memos: TransactionHelper.formatMemos(memos)
213
- };
214
-
215
- if (!allowRippling)
216
- tx.Flags = 131072; // tfSetNoRipple;
217
-
218
- return this.#submitAndVerifyTransaction(tx, options);
219
- }
220
-
221
- setRegularKey(regularKey, memos = null, options = {}) {
222
-
223
- return this.#submitAndVerifyTransaction({
224
- TransactionType: 'SetRegularKey',
225
- Account: this.address,
226
- RegularKey: regularKey,
227
- Memos: TransactionHelper.formatMemos(memos)
228
- }, options);
229
- }
230
-
231
- cashCheck(check, options = {}) {
232
- const checkIDhasher = crypto.createHash('sha512')
233
- checkIDhasher.update(Buffer.from('0043', 'hex'))
234
- checkIDhasher.update(Buffer.from(codec.decodeAccountID(check.Account)))
235
- const seqBuf = Buffer.alloc(4)
236
- seqBuf.writeUInt32BE(check.Sequence, 0)
237
- checkIDhasher.update(seqBuf)
238
- const checkID = checkIDhasher.digest('hex').slice(0, 64).toUpperCase()
239
- console.log("Calculated checkID:", checkID);
240
-
241
- return this.#submitAndVerifyTransaction({
242
- TransactionType: 'CheckCash',
243
- Account: this.address,
244
- CheckID: checkID,
245
- Amount: {
246
- currency: check.SendMax.currency,
247
- issuer: check.SendMax.issuer,
248
- value: check.SendMax.value
249
- },
250
- }, options);
251
- }
252
-
253
- offerSell(sellAmount, sellCurrency, sellIssuer, forAmount, forCurrency, forIssuer = null, expiration = 4294967295, memos = null, options = {}) {
254
-
255
- const sellAmountObj = makeAmountObject(sellAmount, sellCurrency, sellIssuer);
256
- const forAmountObj = makeAmountObject(forAmount, forCurrency, forIssuer);
257
-
258
- return this.#submitAndVerifyTransaction({
259
- TransactionType: 'OfferCreate',
260
- Account: this.address,
261
- TakerGets: sellAmountObj,
262
- TakerPays: forAmountObj,
263
- Expiration: expiration,
264
- Memos: TransactionHelper.formatMemos(memos)
265
- }, options);
266
- }
267
-
268
- offerBuy(buyAmount, buyCurrency, buyIssuer, forAmount, forCurrency, forIssuer = null, expiration = 4294967295, memos = null, options = {}) {
269
-
270
- const buyAmountObj = makeAmountObject(buyAmount, buyCurrency, buyIssuer);
271
- const forAmountObj = makeAmountObject(forAmount, forCurrency, forIssuer);
272
-
273
- return this.#submitAndVerifyTransaction({
274
- TransactionType: 'OfferCreate',
275
- Account: this.address,
276
- TakerGets: forAmountObj,
277
- TakerPays: buyAmountObj,
278
- Expiration: expiration,
279
- Memos: TransactionHelper.formatMemos(memos)
280
- }, options);
281
- }
282
-
283
- cancelOffer(offerSequence, memos = null, options = {}) {
284
- return this.#submitAndVerifyTransaction({
285
- TransactionType: 'OfferCancel',
286
- Account: this.address,
287
- OfferSequence: offerSequence,
288
- Memos: TransactionHelper.formatMemos(memos)
289
- }, options);
290
- }
291
-
292
- mintNft(uri, taxon, transferFee, flags = {}, memos = null, options = {}) {
293
- return this.#submitAndVerifyTransaction({
294
- TransactionType: 'NFTokenMint',
295
- Account: this.address,
296
- URI: flags.isHexUri ? uri : TransactionHelper.asciiToHex(uri).toUpperCase(),
297
- NFTokenTaxon: taxon,
298
- TransferFee: transferFee,
299
- Flags: (flags.isBurnable ? 1 : 0) | (flags.isOnlyXRP ? 2 : 0) | (flags.isTrustLine ? 4 : 0) | (flags.isTransferable ? 8 : 0),
300
- Memos: TransactionHelper.formatMemos(memos)
301
- }, options);
302
- }
303
-
304
- offerSellNft(nfTokenId, amount, currency, issuer = null, destination = null, expiration = 4294967295, memos = null, options = {}) {
305
-
306
- const amountObj = makeAmountObject(amount, currency, issuer);
307
- const tx = {
308
- TransactionType: 'NFTokenCreateOffer',
309
- Account: this.address,
310
- NFTokenID: nfTokenId,
311
- Amount: amountObj,
312
- Expiration: expiration,
313
- Flags: 1, // tfSellToken
314
- Memos: TransactionHelper.formatMemos(memos)
315
- };
316
-
317
- return this.#submitAndVerifyTransaction(destination ? { ...tx, Destination: destination } : tx, options);
318
- }
319
-
320
- offerBuyNft(nfTokenId, owner, amount, currency, issuer = null, expiration = 4294967295, memos = null, options = {}) {
321
-
322
- const amountObj = makeAmountObject(amount, currency, issuer);
323
-
324
- return this.#submitAndVerifyTransaction({
325
- TransactionType: 'NFTokenCreateOffer',
326
- Account: this.address,
327
- NFTokenID: nfTokenId,
328
- Owner: owner,
329
- Amount: amountObj,
330
- Expiration: expiration,
331
- Flags: 0, // Buy offer
332
- Memos: TransactionHelper.formatMemos(memos)
333
- }, options);
334
- }
335
-
336
- sellNft(offerId, memos = null, options = {}) {
337
-
338
- return this.#submitAndVerifyTransaction({
339
- TransactionType: 'NFTokenAcceptOffer',
340
- Account: this.address,
341
- NFTokenBuyOffer: offerId,
342
- Memos: TransactionHelper.formatMemos(memos)
343
- }, options);
344
- }
345
-
346
- buyNft(offerId, memos = null, options = {}) {
347
-
348
- return this.#submitAndVerifyTransaction({
349
- TransactionType: 'NFTokenAcceptOffer',
350
- Account: this.address,
351
- NFTokenSellOffer: offerId,
352
- Memos: TransactionHelper.formatMemos(memos)
353
- }, options);
354
- }
355
-
356
- burnNft(nfTokenId, owner = null, memos = null, options = {}) {
357
-
358
- const tx = {
359
- TransactionType: 'NFTokenBurn',
360
- Account: this.address,
361
- NFTokenID: nfTokenId,
362
- Memos: TransactionHelper.formatMemos(memos)
363
- };
364
-
365
- return this.#submitAndVerifyTransaction(owner ? { ...tx, Owner: owner } : tx, options);
366
- }
367
-
368
- generateKeylet(type, data = {}) {
369
- switch (type) {
370
- case 'nftPage': {
371
- const accIdHex = (codec.decodeAccountID(this.address)).toString('hex').toUpperCase();
372
- const tokenPortion = data?.nfTokenId.substr(40, 64);
373
- return '0050' + accIdHex + tokenPortion;
374
- }
375
-
376
- case 'nftPageMax': {
377
- const accIdHex = (codec.decodeAccountID(this.address)).toString('hex').toUpperCase();
378
- return '0050' + accIdHex + 'F'.repeat(24);
379
- }
380
-
381
- case 'nftPageMin': {
382
- const accIdHex = (codec.decodeAccountID(this.address)).toString('hex').toUpperCase();
383
- return '0050' + accIdHex + '0'.repeat(24);
384
- }
385
-
386
- default:
387
- return null;
388
- }
389
- }
390
-
391
- async subscribe() {
392
- // Subscribe only once. Otherwise event handlers will be duplicated.
393
- if (this.#subscribed)
394
- return;
395
-
396
- await this.xrplApi.subscribeToAddress(this.address, this.#txStreamHandler);
397
-
398
- this.#subscribed = true;
399
- }
400
-
401
- async unsubscribe() {
402
- if (!this.#subscribed)
403
- return;
404
-
405
- await this.xrplApi.unsubscribeFromAddress(this.address, this.#txStreamHandler);
406
- this.#subscribed = false;
407
- }
408
-
409
- #submitAndVerifyTransaction(tx, options) {
410
-
411
- if (!this.wallet)
412
- throw "no_secret";
413
-
414
- // Returned format.
415
- // {
416
- // id: txHash, (if signing success)
417
- // code: final transaction result code.
418
- // details: submission and transaction details, (if signing success)
419
- // error: Any error that prevents submission.
420
- // }
421
-
422
- return new Promise(async (resolve, reject) => {
423
-
424
- // Attach tx options to the transaction.
425
- const txOptions = {
426
- LastLedgerSequence: options.maxLedgerIndex || (this.xrplApi.ledgerIndex + XrplConstants.MAX_LEDGER_OFFSET),
427
- Sequence: options.sequence || await this.getSequence(),
428
- SigningPubKey: '', // This field is required for fee calculation.
429
- Fee: '0' // This field is required for fee calculation.
430
- }
431
- Object.assign(tx, txOptions);
432
- const txnBlob = xrplCodec.encode(tx);
433
- const fees = await this.xrplApi.getTransactionFee(txnBlob);
434
- delete tx['SigningPubKey'];
435
- tx.Fee = fees + '';
436
-
437
- try {
438
- const submission = await this.xrplApi.submitAndVerify(tx, { wallet: this.wallet });
439
- const r = submission?.result;
440
- const txResult = {
441
- id: r?.hash,
442
- code: r?.meta?.TransactionResult,
443
- details: r
444
- };
445
-
446
- console.log("Transaction result: " + txResult.code);
447
- if (txResult.code === "tesSUCCESS")
448
- resolve(txResult);
449
- else
450
- reject(txResult);
451
- }
452
- catch (err) {
453
- console.log("Error submitting transaction:", err);
454
- reject({ error: err });
455
- }
456
-
457
- });
458
- }
459
-
460
- /**
461
- * Submit the signed raw transaction.
462
- * @param txBlob Signed and encoded transacion as a hex string.
463
- */
464
- submitTransactionBlob(txBlob) {
465
-
466
- // Returned format.
467
- // {
468
- // id: txHash, (if signing success)
469
- // code: final transaction result code.
470
- // details: submission and transaction details, (if signing success)
471
- // error: Any error that prevents submission.
472
- // }
473
-
474
- return new Promise(async (resolve, reject) => {
475
- try {
476
- const submission = await this.xrplApi.submitAndVerify(txBlob);
477
- const r = submission?.result;
478
- const txResult = {
479
- id: r?.hash,
480
- code: r?.meta?.TransactionResult,
481
- details: r
482
- };
483
-
484
- console.log("Transaction result: " + txResult.code);
485
- if (txResult.code === "tesSUCCESS")
486
- resolve(txResult);
487
- else
488
- reject(txResult);
489
- }
490
- catch (err) {
491
- console.log("Error submitting transaction:", err);
492
- reject({ error: err });
493
- }
494
-
495
- });
496
- }
497
- }
498
-
499
- function makeAmountObject(amount, currency, issuer) {
500
- if (typeof amount !== 'string')
501
- throw "Amount must be a string.";
502
- if (currency !== XrplConstants.XRP && !issuer)
503
- throw "Non-XRP currency must have an issuer.";
504
-
505
- const amountObj = (currency == XrplConstants.XRP) ? amount : {
506
- currency: currency,
507
- issuer: issuer,
508
- value: amount
509
- }
510
- return amountObj;
511
- }
512
-
513
- module.exports = {
514
- XrplAccount
515
- }