Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update/xrpl dependency #205

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
bb8c579
update xrpl dep to v4
MichaelAJay Oct 23, 2024
6a2843c
add Wallet validator to signTransaction to handle wallets derived fro…
MichaelAJay Oct 23, 2024
8d18e2a
update return from getBlock to match expectation from test
MichaelAJay Oct 23, 2024
a4d5204
add expect for ledger_index type
MichaelAJay Oct 23, 2024
30e4d11
add getTx adapter
MichaelAJay Oct 24, 2024
c373990
correct syntax to CommonJS
MichaelAJay Oct 24, 2024
7b52230
change getRawTransaction retrun from result.tx (undefined) to result.…
MichaelAJay Oct 24, 2024
a60d372
add getTransactions tests block
MichaelAJay Oct 25, 2024
c0747da
finished getTransactions tests
MichaelAJay Oct 25, 2024
67989ad
add expects on type for getTip
MichaelAJay Oct 25, 2024
5823847
remove .only on test
MichaelAJay Oct 25, 2024
67cd2fe
refactor getTransactions to return similar object from xrpl v2
MichaelAJay Oct 28, 2024
b7938f4
expect array instead of empty array to avoid outcome relying on previ…
MichaelAJay Oct 28, 2024
3985088
update balance test to check for number type instead of specific value
MichaelAJay Oct 28, 2024
d8d799c
update package-lock with xrpl v4
MichaelAJay Oct 28, 2024
5a4179b
fix bad dep version
MichaelAJay Nov 6, 2024
c7348bc
fix package-lock
MichaelAJay Nov 6, 2024
43dabfe
update property name in xrp client adapter
MichaelAJay Nov 7, 2024
8acc862
use regular function for Mocha hook to ensure proper context binding
MichaelAJay Nov 8, 2024
a3b12f1
fix return to match expected
MichaelAJay Nov 11, 2024
01fcf33
refactor to make xrp output similar to expected typing from xrpl
MichaelAJay Nov 14, 2024
11684c6
fix erroneous message
MichaelAJay Nov 15, 2024
9a6480a
clean up
MichaelAJay Nov 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 29 additions & 27 deletions lib/xrp/XrpClientAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand All @@ -40,11 +41,11 @@ class XrpClientAdapter extends xrpl.Client {
* @param {number} [options.maxLedgerVersion] - Return only transactions in this ledger version or lower.
* @param {Array<string>} [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<Array>} A promise that resolves to an array of transactions.
* @returns {Promise<xrpl.AccountTxTransaction<2>[]>} 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.
Expand All @@ -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');
}

/**
Expand Down Expand Up @@ -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,
Expand All @@ -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;

Expand All @@ -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
module.exports = XrpClientAdapter;
108 changes: 84 additions & 24 deletions lib/xrp/XrpRpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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<xrpl.Wallet>}
* @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),
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -160,6 +183,8 @@ class XrpRpc {
}

async decodeRawTransaction({ rawTx }) {
// likely {xrpl.Payment}
/** @type {xrpl.BaseTransaction} */
const txJSON = xrpl.decode(rawTx);

if (txJSON.TxnSignature) {
Expand All @@ -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);
}

Expand All @@ -184,10 +212,15 @@ class XrpRpc {
return tip.hash;
}

/**
* @returns {Promise<xrpl.TxResponse['result']['tx_json'] & {confirmations: number; blockHash: string}>}
*/
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
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -248,15 +285,23 @@ class XrpRpc {
}
}

_getBlockByHash({ hash, transactions = true, expand = true }) {
/**
* @returns {Promise<xrpl.LedgerResponse>}
* note: queue_data may not be included on result
*/
async _getBlockByHash({ hash, transactions = true, expand = true }) {
return this.asyncRequest('ledger', {
ledger_hash: hash,
transactions,
expand
});
}

_getBlockByIndex({ index = 'validated', transactions = true, expand = true }) {
/**
* @returns {Promise<xrpl.LedgerResponse>}
* 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,
Expand All @@ -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 };
}

Expand All @@ -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;
Expand All @@ -297,10 +347,20 @@ class XrpRpc {
}
}

/**
* @returns {xrpl.ServerInfoResponse['result']['info']}
*/
async getServerInfo() {
const { result } = await this.asyncRequest('server_info');
return result.info;
}
}

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
*/
Loading