diff --git a/lib/xrp/XrpClientAdapter.js b/lib/xrp/XrpClientAdapter.js index 80ddfd7..dd7abce 100644 --- a/lib/xrp/XrpClientAdapter.js +++ b/lib/xrp/XrpClientAdapter.js @@ -25,10 +25,11 @@ class XrpClientAdapter extends xrpl.Client { } async getLedger({ ledgerVersion }) { - return await this.request({ + const { result } = await this.request({ command: 'ledger', ledger_index: ledgerVersion }); + return result.ledger; } /** @@ -40,11 +41,11 @@ class XrpClientAdapter extends xrpl.Client { * @param {number} [options.maxLedgerVersion] - Return only transactions in this ledger version or lower. * @param {Array} [options.types] - Only return transactions of the specified valid Transaction Types (see SUPPORTED_TRANSACTION_TYPES). * @param {boolean} [options.initiated] - If true, return only transactions initiated by the account specified by acceptanceAddress. If false, return only transactions NOT initiated by the account specified by acceptanceAddress. - * @param {boolean} [options.includeRawTransactions] - Include raw transaction data. For advanced users; exercise caution when interpreting this data. + * @param {boolean} [options.includeRawTransactions] - Include raw transaction data. For advanced users; exercise caution when interpreting this data. DO NOT USE * @param {boolean} [options.excludeFailures] - If true, the result omits transactions that did not succeed. - * @returns {Promise} A promise that resolves to an array of transactions. + * @returns {Promise[]>} A promise that resolves to an array of transactions. * - * @throws {Error} If 'includeRawTransactions' is set to false. + * @throws {Error} If 'includeRawTransactions' is set to true. * @throws {Error} If 'types' is included but not an array. * @throws {Error} If 'types' is included but empty. * @throws {Error} If 'types' includes invalid transaction types. @@ -59,11 +60,11 @@ class XrpClientAdapter extends xrpl.Client { excludeFailures }) { /** - * Behavior defaults to 'true', but this error is to document that 'includeRawTransactions: false' is NOT supported + * Behavior defaults to 'false', but this error is to document that 'includeRawTransactions: true' is NOT supported * Truthiness is not sufficient for this check - it must explicitly be an equality check, & strict equality is prefered */ - if (includeRawTransactions === false) { - throw new Error('"includeRawTransactions: false" not supported'); + if (includeRawTransactions === true) { + throw new Error('"includeRawTransactions: true" not supported'); } /** @@ -93,8 +94,10 @@ class XrpClientAdapter extends xrpl.Client { // Boolean option checks must be checked against type for existence instead of using fallback assignment const INITIATED = typeof initiated === 'boolean' ? initiated : false; const EXCLUDE_FAILURES = typeof excludeFailures === 'boolean' ? excludeFailures : true; - const INCLUDE_RAW_TRANSACTIONS = typeof includeRawTransactions === 'boolean' ? includeRawTransactions : true; + // const INCLUDE_RAW_TRANSACTIONS = typeof includeRawTransactions === 'boolean' ? includeRawTransactions : true; + const INCLUDE_RAW_TRANSACTIONS = false; + /** @type {xrpl.AccountTxResponse} */ const { result } = await this.request({ command: 'account_tx', account: acceptanceAddress, @@ -106,8 +109,8 @@ class XrpClientAdapter extends xrpl.Client { throw new Error('xrpl client request did not return expected form'); } - const filteredTransactions = result.transactions.filter(({ meta, tx }) => { - const { Account: initiatingAccount, TransactionType } = tx; + const filteredTransactions = result.transactions.filter(({ meta, tx_json }) => { + const { Account: initiatingAccount, TransactionType } = tx_json; if (!TYPES.has(TransactionType.toLowerCase())) return false; @@ -129,27 +132,26 @@ class XrpClientAdapter extends xrpl.Client { return true; }); - // Only 'INCLUDE_RAW_TRANSACTIONS: true' is supported - this case is here for future expansion + // Only 'INCLUDE_RAW_TRANSACTIONS: false' is supported if (!INCLUDE_RAW_TRANSACTIONS) { return filteredTransactions; } - return filteredTransactions.map(({ meta, tx, validated }) => { - // ! NOTE ! The raw transaction is missing the 'DeliverMax' property & adds 'LastLedgerSequence' property - const mappedRawTransaction = { - ...tx, - meta, - validated - }; - - // Only rawTransaction used - other transaction properties excluded for simplicity. May be added as required. - return { - rawTransaction: JSON.stringify(mappedRawTransaction) - }; - }); + // INCLUDE_RAW_TRANSACTIONS true here for future expansion + // return filteredTransactions.map(({ meta, tx_json, validated }) => { + // // ! NOTE ! The raw transaction is missing the 'DeliverMax' property & adds 'LastLedgerSequence' property + // const mappedRawTransaction = { + // ...tx_json, + // meta, + // validated + // }; + + // // Only rawTransaction used - other transaction properties excluded for simplicity. May be added as required. + // return { + // rawTransaction: JSON.stringify(mappedRawTransaction) + // }; + // }); } } -module.exports = XrpClientAdapter; - -// Find examples of response at https://xrpl.org/docs/references/http-websocket-apis/public-api-methods/account-methods/account_tx \ No newline at end of file +module.exports = XrpClientAdapter; \ No newline at end of file diff --git a/lib/xrp/XrpRpc.js b/lib/xrp/XrpRpc.js index 15bd118..5c1dd13 100644 --- a/lib/xrp/XrpRpc.js +++ b/lib/xrp/XrpRpc.js @@ -43,9 +43,7 @@ class XrpRpc { // if there is an error connecting, throw error and try again on next call await this.rpc.connect(); } - - let result; - result = await this.rpc[method](args); + const result = await this.rpc[method](args); return result; } @@ -62,8 +60,8 @@ class XrpRpc { } async sendToAddress({ address, amount, passphrase, tag, invoiceID, secret }) { - let rawTx = await this.signTransaction({ address, amount, passphrase, tag, invoiceID, secret }); - let txHash = await this.sendRawTransaction({ rawTx }); + const rawTx = await this.signTransaction({ address, amount, passphrase, tag, invoiceID, secret }); + const txHash = await this.sendRawTransaction({ rawTx }); return txHash; } @@ -73,8 +71,30 @@ class XrpRpc { err.conclusive = true; // used by server throw err; } - let wallet = xrpl.Wallet.fromSeed(secret); - let payment = { + + /** + * Generates wallet & returns it if Account exists + * @param {string} seed + * @param {xrpl.ECDSA} algorithm + * + * @returns {Promise} + * @throws {xrpl.XrplError} - If the account does not exist (data.error_code = 19) + */ + const getWalletFromSeedAndConfirmAccount = async (seed, algorithm = xrpl.ECDSA.ed25519) => { + const wallet = xrpl.Wallet.fromSeed(seed, { algorithm }); + await this.asyncRequest('account_info', { account: wallet.address }); + return wallet; + }; + + let wallet; + try { + wallet = await getWalletFromSeedAndConfirmAccount(secret); + } catch (err) { + wallet = await getWalletFromSeedAndConfirmAccount(secret, xrpl.ECDSA.secp256k1); + } + + /** @type {xrpl.SubmittableTransaction} */ + const payment = { TransactionType: 'Payment', Account: wallet.address, Amount: xrpl.xrpToDrops(amount), @@ -136,11 +156,11 @@ class XrpRpc { return resultArray; } - async getRawTransaction({ txid }) { try { - const { result } = await this.asyncRequest('tx', { transaction: txid, binary: true }); - return result.tx; + /** @type {AccountTransactionBinaryResult} */ + const result = (await this.asyncRequest('tx', { transaction: txid, binary: true })).result; + return result.tx_blob; } catch (err) { if (err && err.data && err.data.error === 'txnNotFound') { return null; @@ -149,8 +169,11 @@ class XrpRpc { } } + // todo async sendRawTransaction({ rawTx }) { - const { result } = await this.asyncCall('submit', rawTx); + /** @type {xrpl.SubmitResponse} */ + const response = await this.asyncCall('submit', rawTx); + const result = response.result; const { accepted, engine_result_message, tx_json } = result; if (accepted) { return tx_json.hash; @@ -160,6 +183,8 @@ class XrpRpc { } async decodeRawTransaction({ rawTx }) { + // likely {xrpl.Payment} + /** @type {xrpl.BaseTransaction} */ const txJSON = xrpl.decode(rawTx); if (txJSON.TxnSignature) { @@ -170,12 +195,15 @@ class XrpRpc { } async estimateFee() { - const { result } = await this.asyncRequest('fee'); + const response = await this.asyncRequest('fee'); + /** @type {xrpl.FeeResponse['result']} */ + const result = response.result; // NOTE - result is missing expected max_queue_size return result.drops.minimum_fee; } async getBalance({ address }) { - let balance = await this.asyncCall('getXrpBalance', address || this.address); + /** @type {number} integer */ + const balance = await this.asyncCall('getXrpBalance', address || this.address); return parseFloat(balance); } @@ -184,10 +212,15 @@ class XrpRpc { return tip.hash; } + /** + * @returns {Promise} + */ async getTransaction({ txid }) { try { - const { result } = await this.asyncRequest('tx', { transaction: txid }); - if (!result) { + /** @type {xrpl.TxResponse} */ + const response = await this.asyncRequest('tx', { transaction: txid }); + const result = response.result; + if (!result || !result.validated) { return null; } // Append Confirmations @@ -199,7 +232,8 @@ class XrpRpc { result.confirmations = height - result.ledger_index + 1; // Tip is considered confirmed } // Append BlockHash - const { result: txBlock } = await this.asyncRequest('ledger', { ledger_index: result.ledger_index }); + /** @type {xrpl.LedgerResponse['result']} */ + const txBlock = (await this.asyncRequest('ledger', { ledger_index: result.ledger_index })).result; result.blockHash = txBlock.ledger_hash; return result; } catch (err) { @@ -214,10 +248,13 @@ class XrpRpc { * Get all transactions for an account * @param {string} address Account to get transactions for * @param {object} options See https://xrpl.org/docs/references/http-websocket-apis/public-api-methods/account-methods/account_tx/ - * @returns + * @returns {xrpl.AccountTxResponse['result']} */ async getTransactions({ address, options }) { try { + await this.asyncRequest('account_info', { account: address }); // In xrpl v4, account_tx does not error on unfound account + + /** @type {xrpl.AccountTxResponse} */ const { result } = await this.asyncRequest('account_tx', { account: address, ...options }); return result; } catch (err) { @@ -248,7 +285,11 @@ class XrpRpc { } } - _getBlockByHash({ hash, transactions = true, expand = true }) { + /** + * @returns {Promise} + * note: queue_data may not be included on result + */ + async _getBlockByHash({ hash, transactions = true, expand = true }) { return this.asyncRequest('ledger', { ledger_hash: hash, transactions, @@ -256,7 +297,11 @@ class XrpRpc { }); } - _getBlockByIndex({ index = 'validated', transactions = true, expand = true }) { + /** + * @returns {Promise} + * note: queue_data may not be included on result + */ + async _getBlockByIndex({ index = 'validated', transactions = true, expand = true }) { return this.asyncRequest('ledger', { ledger_index: index, transactions, @@ -269,11 +314,11 @@ class XrpRpc { } async getTip() { - const { result } = await this.asyncRequest('ledger', { + /** @type {xrpl.LedgerResponse} */ + const response = await this.asyncRequest('ledger', { ledger_index: 'validated' }); - let height = result.ledger_index; - let hash = result.ledger_hash; + const { ledger_index: height, ledger_hash: hash } = response.result; return { height, hash }; } @@ -285,10 +330,15 @@ class XrpRpc { return xrpl.isValidAddress(address); } + /** + * @return {xrpl.ACcountInfoResponse['result']} + * will not include ledger_index or queue_data + */ async getAccountInfo({ address }) { try { - const { result } = await this.asyncRequest('account_info', { account: address }); - return result; + /** @type {xrpl.AccountInfoResponse} */ + const response = await this.asyncRequest('account_info', { account: address }); + return response.result; } catch (error) { if (error.data && error.data.error && error.data.error === 'actNotFound') { error.conclusive = true; @@ -297,6 +347,9 @@ class XrpRpc { } } + /** + * @returns {xrpl.ServerInfoResponse['result']['info']} + */ async getServerInfo() { const { result } = await this.asyncRequest('server_info'); return result.info; @@ -304,3 +357,10 @@ class XrpRpc { } module.exports = XrpRpc; + +/** + * @typedef {Object} AccountTransactionBinaryResult + * @property {string} hash - Transaction hash + * @property {string} tx_blob - Transaction blob + * @property {boolean} validated - Indicates if the transaction is validated + */ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 62aa9bc..a614864 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "lightning": "10.0.1", "promptly": "0.2.0", "web3": "1.7.1", - "xrpl": "^2.6.0" + "xrpl": "4.0.0" }, "devDependencies": { "assert": "^1.4.1", @@ -956,22 +956,25 @@ "dev": true }, "node_modules/@noble/curves": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.5.0.tgz", - "integrity": "sha512-J5EKamIHnKPyClwVrzmaf5wSdQXgdHcPZIZLu3bwnbeCx8/7NPK5q2ZBWF+5FvYGByjiQQsJYX6jfgB2wDPn3A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.6.0.tgz", + "integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==", "dependencies": { - "@noble/hashes": "1.4.0" + "@noble/hashes": "1.5.0" + }, + "engines": { + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", "engines": { - "node": ">= 16" + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -1066,6 +1069,42 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, + "node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.5.0.tgz", + "integrity": "sha512-8EnFYkqEQdnkuGBVpCzKxyIwDCBLDVj3oiX0EKUFre/tOjL/Hqba1D6n/8RcmaQy4f95qQFrO2A8Sr6ybh4NRw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.6.0", + "@noble/hashes": "~1.5.0", + "@scure/base": "~1.1.7" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.4.0.tgz", + "integrity": "sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.5.0", + "@scure/base": "~1.1.8" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -1653,6 +1692,57 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@xrplf/isomorphic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@xrplf/isomorphic/-/isomorphic-1.0.1.tgz", + "integrity": "sha512-0bIpgx8PDjYdrLFeC3csF305QQ1L7sxaWnL5y71mCvhenZzJgku9QsA+9QCXBC1eNYtxWO/xR91zrXJy2T/ixg==", + "license": "ISC", + "dependencies": { + "@noble/hashes": "^1.0.0", + "eventemitter3": "5.0.1", + "ws": "^8.13.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@xrplf/isomorphic/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/@xrplf/isomorphic/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@xrplf/secret-numbers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@xrplf/secret-numbers/-/secret-numbers-1.0.0.tgz", + "integrity": "sha512-qsCLGyqe1zaq9j7PZJopK+iGTGRbk6akkg6iZXJJgxKwck0C5x5Gnwlb1HKYGOwPKyrXWpV6a2YmcpNpUFctGg==", + "license": "ISC", + "dependencies": { + "@xrplf/isomorphic": "^1.0.0", + "ripple-keypairs": "^2.0.0" + } + }, "node_modules/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -2033,14 +2123,6 @@ "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/bigint-buffer": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", @@ -2077,44 +2159,6 @@ "node": ">=8.0.0" } }, - "node_modules/bip32": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", - "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", - "dependencies": { - "@types/node": "10.12.18", - "bs58check": "^2.1.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "tiny-secp256k1": "^1.1.3", - "typeforce": "^1.11.5", - "wif": "^2.0.6" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/bip32/node_modules/@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" - }, - "node_modules/bip39": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz", - "integrity": "sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==", - "dependencies": { - "@types/node": "11.11.6", - "create-hash": "^1.1.0", - "pbkdf2": "^3.0.9", - "randombytes": "^2.0.1" - } - }, - "node_modules/bip39/node_modules/@types/node": { - "version": "11.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", - "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" - }, "node_modules/bip66": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", @@ -2981,6 +3025,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -3297,11 +3342,6 @@ "es6-symbol": "^3.1.1" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" - }, "node_modules/es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -5266,21 +5306,6 @@ "npm": ">=3" } }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -5719,7 +5744,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -6064,11 +6090,6 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, - "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - }, "node_modules/nano-json-stream-parser": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", @@ -6212,25 +6233,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "engines": { "node": ">= 0.4" } @@ -6969,36 +6976,45 @@ "inherits": "^2.0.1" } }, - "node_modules/ripple-keypairs": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-1.1.4.tgz", - "integrity": "sha512-PMMjTOxZmCSBOvHPj6bA+V/HGx7oFgDtGGI8VcZYuaFO2H87UX0X0jhfHy+LA2Xy31WYlD7GaDIDDt2QO+AMtw==", + "node_modules/ripple-address-codec": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-5.0.0.tgz", + "integrity": "sha512-de7osLRH/pt5HX2xw2TRJtbdLLWHu0RXirpQaEeCnWKY5DYHykh3ETSkofvm0aX0LJiV7kwkegJxQkmbO94gWw==", + "license": "ISC", "dependencies": { - "bn.js": "^5.1.1", - "brorand": "^1.0.5", - "elliptic": "^6.5.4", - "hash.js": "^1.0.3", - "ripple-address-codec": "^4.2.4" + "@scure/base": "^1.1.3", + "@xrplf/isomorphic": "^1.0.0" }, "engines": { - "node": ">= 10" + "node": ">= 16" } }, - "node_modules/ripple-keypairs/node_modules/bn.js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", - "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + "node_modules/ripple-binary-codec": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-2.1.0.tgz", + "integrity": "sha512-q0GAx+hj3UVcDbhXVjk7qeNfgUMehlElYJwiCuIBwqs/51GVTOwLr39Ht3eNsX5ow2xPRaC5mqHwcFDvLRm6cA==", + "license": "ISC", + "dependencies": { + "@xrplf/isomorphic": "^1.0.1", + "bignumber.js": "^9.0.0", + "ripple-address-codec": "^5.0.0" + }, + "engines": { + "node": ">= 16" + } }, - "node_modules/ripple-keypairs/node_modules/ripple-address-codec": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-4.2.4.tgz", - "integrity": "sha512-roAOjKz94+FboTItey1XRh5qynwt4xvfBLvbbcx+FiR94Yw2x3LrKLF2GVCMCSAh5I6PkcpADg6AbYsUbGN3nA==", + "node_modules/ripple-keypairs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-2.0.0.tgz", + "integrity": "sha512-b5rfL2EZiffmklqZk1W+dvSy97v3V/C7936WxCCgDynaGPp7GE6R2XO7EU9O2LlM/z95rj870IylYnOQs+1Rag==", + "license": "ISC", "dependencies": { - "base-x": "3.0.9", - "create-hash": "^1.1.2" + "@noble/curves": "^1.0.0", + "@xrplf/isomorphic": "^1.0.0", + "ripple-address-codec": "^5.0.0" }, "engines": { - "node": ">= 10" + "node": ">= 16" } }, "node_modules/rlp": { @@ -7822,22 +7838,6 @@ "node": ">=0.10.0" } }, - "node_modules/tiny-secp256k1": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", - "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", - "hasInstallScript": true, - "dependencies": { - "bindings": "^1.3.0", - "bn.js": "^4.11.8", - "create-hmac": "^1.1.7", - "elliptic": "^6.4.0", - "nan": "^2.13.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/to-readable-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", @@ -8774,140 +8774,30 @@ } }, "node_modules/xrpl": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-2.6.0.tgz", - "integrity": "sha512-++rKtgO1j65TMm//mird3aGFGFYUn7VP9TjxwJkTUQZOdWkMUfiG2cd2o3tZ/zE07Ev8YVD09KgMZ9HzynJN9Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-4.0.0.tgz", + "integrity": "sha512-VZm1lQWHQ6PheAAFGdH+ISXKvqB2hZDQ0w4ZcdAEtmqZQXtSIVQHOKPz95rEgGANbos7+XClxJ73++joPhA8Cw==", + "license": "ISC", "dependencies": { + "@scure/bip32": "^1.3.1", + "@scure/bip39": "^1.2.1", + "@xrplf/isomorphic": "^1.0.1", + "@xrplf/secret-numbers": "^1.0.0", "bignumber.js": "^9.0.0", - "bip32": "^2.0.6", - "bip39": "^3.0.4", - "https-proxy-agent": "^5.0.0", - "lodash": "^4.17.4", - "ripple-address-codec": "^4.2.4", - "ripple-binary-codec": "^1.4.2", - "ripple-keypairs": "^1.1.4", - "ws": "^8.2.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/xrpl/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/xrpl/node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", - "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" - } - }, - "node_modules/xrpl/node_modules/buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "node_modules/xrpl/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" + "eventemitter3": "^5.0.1", + "ripple-address-codec": "^5.0.0", + "ripple-binary-codec": "^2.1.0", + "ripple-keypairs": "^2.0.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18.0.0" } }, - "node_modules/xrpl/node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" - }, - "node_modules/xrpl/node_modules/https-proxy-agent": { + "node_modules/xrpl/node_modules/eventemitter3": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/xrpl/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/xrpl/node_modules/ripple-address-codec": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-4.2.4.tgz", - "integrity": "sha512-roAOjKz94+FboTItey1XRh5qynwt4xvfBLvbbcx+FiR94Yw2x3LrKLF2GVCMCSAh5I6PkcpADg6AbYsUbGN3nA==", - "dependencies": { - "base-x": "3.0.9", - "create-hash": "^1.1.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/xrpl/node_modules/ripple-binary-codec": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.4.2.tgz", - "integrity": "sha512-EDKIyZMa/6Ay/oNgCwjD9b9CJv0zmBreeHVQeG4BYwy+9GPnIQjNeT5e/aB6OjAnhcmpgbPeBmzwmNVwzxlt0w==", - "dependencies": { - "assert": "^2.0.0", - "big-integer": "^1.6.48", - "buffer": "5.6.0", - "create-hash": "^1.2.0", - "decimal.js": "^10.2.0", - "ripple-address-codec": "^4.2.4" - }, - "engines": { - "node": ">=10.22.0" - } - }, - "node_modules/xrpl/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" }, "node_modules/xtend": { "version": "4.0.2", diff --git a/package.json b/package.json index 9d78baf..273cd5a 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lightning": "10.0.1", "promptly": "0.2.0", "web3": "1.7.1", - "xrpl": "^2.6.0" + "xrpl": "4.0.0" }, "devDependencies": { "assert": "^1.4.1", diff --git a/tests/bch.js b/tests/bch.js index 840fa10..81c3886 100644 --- a/tests/bch.js +++ b/tests/bch.js @@ -28,7 +28,7 @@ describe('BCH Tests', function() { const rpcs = new CryptoRpc(config, currencyConfig); const bitcoin = rpcs.get(currency); - before(async () => { + before(async function() { try { await bitcoin.asyncCall('encryptWallet', ['password']); } catch (e) { diff --git a/tests/xrp.js b/tests/xrp.js index 048c9cd..1a5db49 100644 --- a/tests/xrp.js +++ b/tests/xrp.js @@ -1,3 +1,5 @@ +// eslint-disable-next-line no-unused-vars +const xrpl = require('xrpl'); // Here for typing const { CryptoRpc } = require('../'); const {assert, expect} = require('chai'); const mocha = require('mocha'); @@ -5,7 +7,8 @@ const {describe, it, before} = mocha; const config = { chain: 'XRP', currency: 'XRP', - host: 'rippled', + // host: 'rippled', + host: 'localhost', protocol: 'ws', rpcPort: '6006', address: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', @@ -63,12 +66,14 @@ describe('XRP Tests', function() { for (const bcase of blockCases) { it(`should get block ${bcase.description}`, async () => { try { + /** @type {xrpl.LedgerResponse['result'] | null}} */ block = await rpcs.getBlock({ currency, ...bcase.params }); } catch (err) { expect(err).to.not.exist(); } expect(block).to.have.property('ledger'); + /** @type {xrpl.LedgerBinary} */ let ledger = block.ledger; // from xrpl documentation: https://xrpl.org/ledger.html (9/26/2023) // The following fields are deprecated and may be removed without further notice: accepted, totalCoins (use total_coins instead). @@ -79,11 +84,12 @@ describe('XRP Tests', function() { expect(ledger).to.have.property('ledger_index'); expect(ledger).to.have.property('parent_hash'); expect(ledger).to.have.property('transactions'); - expect(ledger.transactions).to.deep.equal([]); + expect(ledger).to.have.property('transactions').that.is.an('array'); expect(block).to.have.property('ledger_hash'); expect(block).to.have.property('ledger_index'); expect(block.ledger_hash).to.equal(ledger.ledger_hash); - expect(block.ledger_index.toString()).to.equal(ledger.ledger_index); + expect(typeof block.ledger_index).to.equal('number'); + expect(block.ledger_index).to.equal(ledger.ledger_index); expect(block).to.have.property('validated'); expect(block.validated).to.equal(true); assert(block); @@ -107,7 +113,7 @@ describe('XRP Tests', function() { } catch (err) { expect(err).to.not.exist(); } - expect(balance).to.eq(100000000000); + expect(balance).to.be.a('number'); assert(balance != undefined); }); @@ -217,23 +223,41 @@ describe('XRP Tests', function() { }); it('should be able to get a transaction', async () => { + /** @type {xrpl.TxResponse['result'] & {confirmations: number; blockHash: string}} */ let tx; try { tx = await rpcs.getTransaction({ currency, txid }); } catch (err) { expect(err).to.not.exist(); } - expect(tx).to.have.property('Account'); - expect(tx).to.have.property('Amount'); - expect(tx).to.have.property('Destination'); - expect(tx).to.have.property('Fee'); - expect(tx).to.have.property('Flags'); - expect(tx).to.have.property('LastLedgerSequence'); - expect(tx).to.have.property('Sequence'); + // New expectations + expect(tx).to.have.property('tx_json'); + expect(tx.tx_json).to.have.property('Account'); + expect(tx.tx_json).to.have.property('Destination'); + expect(tx.tx_json).to.have.property('Fee'); + expect(tx.tx_json).to.have.property('Flags'); + expect(tx.tx_json).to.have.property('LastLedgerSequence'); + expect(tx.tx_json).to.have.property('Sequence'); + expect(tx).to.have.property('hash'); expect(tx.hash).to.equal(txid); - expect(tx).to.have.property('blockHash'); - expect(tx.blockHash).to.not.be.undefined; + expect(tx).to.have.property('blockHash').that.is.a('string'); + + expect(tx).to.have.property('meta'); + expect(tx.meta).to.have.property('delivered_amount').that.is.a('string'); + + // Old expectations + // expect(tx).to.have.property('Account'); + // expect(tx).to.have.property('Amount'); + // expect(tx).to.have.property('Destination'); + // expect(tx).to.have.property('Fee'); + // expect(tx).to.have.property('Flags'); + // expect(tx).to.have.property('LastLedgerSequence'); + // expect(tx).to.have.property('Sequence'); + // expect(tx).to.have.property('blockHash'); + // expect(tx.hash).to.equal(txid); + // expect(tx).to.have.property('blockHash'); + // expect(tx.blockHash).to.not.be.undefined; }); it('should return nothing for unknown transaction', async () => { @@ -266,6 +290,127 @@ describe('XRP Tests', function() { expect(tx === null); }); + describe('getTransactions', () => { + const sender = config.address; + const senderSeed = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'; + const recipient1 = 'r38UsJxHSJKajC8qcNmofxJvCESnzmx7Ke'; + const recipient2 = 'rMGhv5SNsk81QN1fGu6RybDkUi2of36dua'; + const recipient3 = 'r4ip6t3NUe4UWguLUJCbyojxG6PdPZg9EJ'; + const recipient4 = 'rwtFtAMNXPoq4xgxn3FzKKGgVZErdcuLST'; + const senderAddress_ed25519 = 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf'; + + before(async () => { + await xrpRPC.rpc.connect(); + const initialServerInfo = await xrpRPC.rpc.request({ command: 'server_info' }); + const initialSeq = initialServerInfo.result.info.validated_ledger.seq; + + const payToArray = [recipient1, recipient2, recipient3, recipient4] + .map((recipient, index) => ({ id: index, address: recipient, amount: 10000 * (index + 1) })); + const eventEmitter = rpcs.rpcs.XRP.emitter; + let eventCounter = 0; + let emitResults = []; + const emitPromise = new Promise(resolve => { + eventEmitter.on('success', (emitData) => { + eventCounter++; + emitResults.push(emitData); + if (eventCounter === 3) { + resolve(); + } + }); + }); + const outputArray = await rpcs.unlockAndSendToAddressMany({ payToArray, secret: senderSeed }); + await emitPromise; + expect(outputArray).to.have.lengthOf(4); + await xrpRPC.rpc.request({ command: 'ledger_accept' }); + + let currentSeq; + do { + const response = await xrpRPC.rpc.request({ command: 'server_info' }); + currentSeq = response.result.info.validated_ledger.seq; + await new Promise(resolve => setTimeout(resolve, 100)); + } while (currentSeq <= initialSeq); + }); + it('should be able to get many transactions', async () => { + const address = sender; + /** @type {xrpl.AccountTxResponse['result']} */ + const result = await xrpRPC.getTransactions({ address }); + expect(result).to.have.property('account', address); + expect(result).to.have.property('ledger_index_max').that.is.a('number'); + expect(result).to.have.property('ledger_index_min').that.is.a('number'); + expect(result).to.have.property('limit').that.is.a('number'); + expect(result).to.have.property('transactions').that.is.an('array'); + expect(result).to.have.property('validated').that.is.a('boolean'); + expect(result.transactions).to.have.lengthOf.above(0); + + const tx = result.transactions[0]; + // New expectations + expect(tx).to.have.property('tx_json'); + expect(tx.tx_json).to.have.property('Account'); + expect(tx.tx_json).to.have.property('Destination'); + expect(tx.tx_json).to.have.property('Fee'); + expect(tx.tx_json).to.have.property('Flags'); + expect(tx.tx_json).to.have.property('LastLedgerSequence'); + expect(tx.tx_json).to.have.property('Sequence'); + + expect(tx).to.have.property('hash'); + // expect(tx.hash).to.equal(txid); + // expect(tx).to.have.property('blockHash').that.is.a('string'); + + expect(tx).to.have.property('meta'); + expect(tx.meta).to.have.property('delivered_amount').that.is.a('string'); + + // Old expectations + // expect(tx).to.have.property('meta'); + // expect(tx).to.have.property('tx'); + // expect(tx.meta).to.have.property('delivered_amount').that.is.a('string'); + // expect(tx.tx).to.have.property('Account').that.is.a('string'); + // expect(tx.tx).to.have.property('Amount').that.is.a('string'); + // expect(tx.tx).to.have.property('DeliverMax').that.is.a('string'); + // expect(tx.tx).to.have.property('Destination').that.is.a('string'); + // expect(tx.tx).to.have.property('Fee').that.is.a('string'); + // expect(tx.tx).to.have.property('Flags').that.is.a('number'); + // expect(tx.tx).to.have.property('LastLedgerSequence').that.is.a('number'); + // expect(tx.tx).to.have.property('Sequence').that.is.a('number'); + // expect(tx.tx).to.have.property('SigningPubKey').that.is.a('string'); + // expect(tx.tx).to.have.property('TransactionType', 'Payment'); + // expect(tx.tx).to.have.property('TxnSignature').that.is.a('string'); + // expect(tx.tx).to.have.property('date').that.is.a('number'); + // expect(tx.tx).to.have.property('hash').that.is.a('string'); + // expect(tx.tx).to.have.property('inLedger').that.is.a('number'); + // expect(tx.tx).to.have.property('ledger_index').that.is.a('number'); + }); + it('should return an empty array if account isn\'t found', async () => { + // Public key from wrong algorithm + const result = await xrpRPC.getTransactions({ address: senderAddress_ed25519 }); + expect(result).to.be.an('array'); + expect(result).to.have.lengthOf(0); + }); + it('should throw RippledError: Missing field "account" if address undefined', async () => { + try { + await xrpRPC.getTransactions({ address: undefined }); + expect.fail('Expected error was not thrown'); + } catch (err) { + expect(err).to.have.property('data'); + const { data } = err; + expect(data).to.have.property('error', 'invalidParams'); + expect(data).to.have.property('error_code', 31); + expect(data).to.have.property('error_message', 'Missing field \'account\'.'); + } + }); + it('should throw RippledError: Account malformed if bad address', async () => { + try { + await xrpRPC.getTransactions({ address: 'badAddress' }); + expect.fail('Expected error was not thrown'); + } catch (err) { + expect(err).to.have.property('data'); + const { data } = err; + expect(data).to.have.property('error', 'actMalformed'); + expect(data).to.have.property('error_code', 35); + expect(data).to.have.property('error_message', 'Account malformed.'); + } + }); + }); + it('should be able to decode a raw transaction', async () => { const { rawTx } = config.currencyConfig; assert(rawTx); @@ -284,8 +429,8 @@ describe('XRP Tests', function() { it('should get the tip', async () => { const tip = await rpcs.getTip({ currency }); assert(tip != undefined); - expect(tip).to.have.property('hash'); - expect(tip).to.have.property('height'); + expect(tip).to.have.property('hash').that.is.a('string'); + expect(tip).to.have.property('height').that.is.a('number'); }); it('should get confirmations', async () => {