evernode-js-client 0.5.10 → 0.5.11
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.
- package/.eslintrc.json +14 -0
- package/LICENSE +21 -0
- package/README.md +25 -3
- package/clean-pkg.sh +4 -0
- package/npm-readme.md +4 -0
- package/package.json +10 -1
- package/remove-versions.sh +10 -0
- package/src/clients/base-evernode-client.js +609 -0
- package/src/clients/host-client.js +560 -0
- package/src/clients/registry-client.js +54 -0
- package/src/clients/tenant-client.js +276 -0
- package/src/defaults.js +21 -0
- package/src/eccrypto.js +258 -0
- package/src/encryption-helper.js +96 -0
- package/src/event-emitter.js +45 -0
- package/src/evernode-common.js +113 -0
- package/src/evernode-helpers.js +45 -0
- package/src/firestore/firestore-handler.js +309 -0
- package/src/index.js +37 -0
- package/src/state-helpers.js +396 -0
- package/src/transaction-helper.js +62 -0
- package/src/util-helpers.js +50 -0
- package/src/xfl-helpers.js +130 -0
- package/src/xrpl-account.js +515 -0
- package/src/xrpl-api.js +301 -0
- package/src/xrpl-common.js +17 -0
- package/test/package-lock.json +884 -0
- package/test/package.json +9 -0
- package/test/test.js +409 -0
- package/index.js +0 -15876
@@ -0,0 +1,515 @@
|
|
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
|
+
}
|