evernode-js-client 0.5.0 → 0.5.2

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