@bithomp/xrpl-api
Version:
A Bithomp JavaScript/TypeScript library for interacting with the XRP Ledger
365 lines (364 loc) • 14.5 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTransactions = getTransactions;
exports.findTransactionsExt = findTransactionsExt;
exports.findTransactions = findTransactions;
const lodash_1 = __importDefault(require("lodash"));
const Client = __importStar(require("../client"));
const utils_1 = require("../common/utils");
const transaction_1 = require("../models/transaction");
const balance_changes_1 = require("../parse/outcome/balance_changes");
const MAX_LIMIT = 1000;
const DEFAULT_LIMIT = 200;
const MAX_LIMIT_WITH_FILTER = 20;
const MAX_LIMIT_WITH_TAG = 3;
const LIMIT_INCREASE_COUNT = 10;
async function getTransactions(account, options = { limit: DEFAULT_LIMIT }) {
const { hash, marker } = (0, utils_1.parseMarker)(options.marker);
options.marker = marker;
const connection = Client.findConnection("history", undefined, undefined, hash);
if (!connection) {
throw new Error("There is no connection");
}
const command = {
command: "account_tx",
account,
ledger_index_min: options.ledgerIndexMin,
ledger_index_max: options.ledgerIndexMax,
ledger_hash: options.ledgerHash,
ledger_index: options.ledgerIndex,
binary: !!options.binary,
forward: !!options.forward,
limit: options.limit,
marker: options.marker,
};
const response = await connection.request(command);
if (!response) {
return {
account,
status: "error",
error: "invalidResponse",
};
}
if (response.error) {
const { error, error_code, error_message, error_exception, status, validated, warnings } = response;
return (0, utils_1.removeUndefined)({
account,
error,
error_code,
error_message,
error_exception,
status,
validated,
warnings,
});
}
const result = response?.result;
if (!result) {
return (0, utils_1.removeUndefined)({
account,
status: "error",
error: "invalidResponse",
warnings: response.warnings,
});
}
if (Array.isArray(result.transactions)) {
if (options.balanceChanges === true || options.specification === true) {
for (const transaction of result.transactions) {
if (options.balanceChanges === true) {
transaction.balanceChanges = (0, balance_changes_1.parseBalanceChanges)(transaction.meta, Client.getNativeCurrency());
}
if (options.specification === true) {
const details = (0, transaction_1.getAccountTxDetails)(transaction, options.includeRawTransactions);
transaction.specification = details.specification;
transaction.outcome = details.outcome;
if (details.rawTransaction) {
transaction.rawTransaction = details.rawTransaction;
}
}
}
}
}
if (Array.isArray(result.transactions)) {
for (const transaction of result.transactions) {
const tx = transaction.tx || transaction;
if (!tx.hasOwnProperty("ctid")) {
const ctid = (0, transaction_1.encodeCTIDforTransaction)(transaction, connection.getNetworkID());
if (ctid) {
tx.ctid = ctid;
}
}
}
}
const newMarker = (0, utils_1.createMarker)(connection.hash, result.marker);
if (newMarker) {
result.marker = newMarker;
}
if (response.warnings && response.warnings.length > 0) {
result.warnings = response.warnings;
}
result.bithompHash = connection.hash;
return result;
}
async function findTransactionsExt(account, options = { limit: DEFAULT_LIMIT, timeout: 15000 }) {
let transactions = [];
let accountTransactionsError = null;
const timeStart = new Date();
const loadOptions = { ...options };
const formatted = loadOptions.legacy === true || loadOptions.formatted === true;
loadOptions.binary = false;
applyLimitOptions(loadOptions);
await applyStartTxOptions(loadOptions);
let getTransactionsLimit = loadOptions.limit;
if (transactions.length === 0 && loadOptions.startTxHash) {
getTransactionsLimit += LIMIT_INCREASE_COUNT;
}
if (loadOptions.sourceTag || loadOptions.destinationTag) {
getTransactionsLimit += LIMIT_INCREASE_COUNT;
}
if (loadOptions.types) {
getTransactionsLimit += LIMIT_INCREASE_COUNT;
}
if (getTransactionsLimit > MAX_LIMIT) {
getTransactionsLimit = MAX_LIMIT;
}
while (transactions.length !== loadOptions.limit) {
const currentTime = new Date();
if (loadOptions.timeout && currentTime.getTime() - timeStart.getTime() > loadOptions.timeout) {
break;
}
const accountTransactions = await getTransactions(account, {
...loadOptions,
...{ balanceChanges: false, specification: false, limit: getTransactionsLimit },
});
if (accountTransactions.error) {
if (accountTransactions.error_message === "Request timeout.") {
continue;
}
else {
accountTransactionsError = accountTransactions;
break;
}
}
let newTransactions = accountTransactions.transactions;
loadOptions.marker = accountTransactions.marker;
newTransactions = newTransactions
.filter(lodash_1.default.partial(filterHelperTransactions, account, loadOptions))
.filter(lodash_1.default.partial(filterHelperStartTx, loadOptions));
if (formatted !== true && (loadOptions.balanceChanges === true || loadOptions.specification === true)) {
for (const newTransaction of newTransactions) {
if (loadOptions.balanceChanges === true) {
newTransaction.balanceChanges = (0, balance_changes_1.parseBalanceChanges)(newTransaction.meta, Client.getNativeCurrency());
}
if (loadOptions.specification === true) {
const details = (0, transaction_1.getAccountTxDetails)(newTransaction, options.includeRawTransactions);
newTransaction.specification = details.specification;
newTransaction.outcome = details.outcome;
if (details.rawTransaction) {
newTransaction.rawTransaction = details.rawTransaction;
}
}
}
}
if (newTransactions.length === 0 && getTransactionsLimit < MAX_LIMIT) {
getTransactionsLimit += LIMIT_INCREASE_COUNT;
if (getTransactionsLimit > MAX_LIMIT) {
getTransactionsLimit = MAX_LIMIT;
}
}
if (newTransactions.length > 0) {
const transactionsToTake = loadOptions.limit - transactions.length;
if (transactionsToTake !== newTransactions.length) {
const isClio = accountTransactions.warnings?.some((w) => w.id === 2001);
let markerTransaction = null;
if (isClio) {
markerTransaction = newTransactions[transactionsToTake - 1];
}
else {
markerTransaction = newTransactions[transactionsToTake];
}
if (markerTransaction) {
const bithompHash = loadOptions.marker?.bithompHash || accountTransactions.bithompHash;
loadOptions.marker = {
ledger: markerTransaction.tx.ledger_index,
seq: markerTransaction.meta.TransactionIndex,
};
if (bithompHash) {
loadOptions.marker.bithompHash = bithompHash;
}
}
}
newTransactions = newTransactions.slice(0, transactionsToTake);
transactions = transactions.concat(newTransactions);
}
if (loadOptions.marker === undefined) {
break;
}
}
if (accountTransactionsError && transactions.length === 0) {
return accountTransactionsError;
}
if (loadOptions.marker && transactions.length === 0) {
return {
status: "error",
error: "searchTimeout",
marker: loadOptions.marker,
};
}
if (formatted === true) {
transactions = transactions.map((transaction) => (0, transaction_1.getAccountTxDetails)(transaction, loadOptions.includeRawTransactions === true));
}
return {
account,
transactions,
marker: loadOptions.marker,
validated: true,
};
}
async function findTransactions(account, options = { limit: DEFAULT_LIMIT, timeout: 15000 }) {
const result = await findTransactionsExt(account, options);
if (result.error) {
return result;
}
return result.transactions || [];
}
function applyLimitOptions(options) {
if (options.sourceTag > 0 || options.destinationTag > 0) {
if (options.limit > MAX_LIMIT_WITH_TAG) {
options.limit = MAX_LIMIT_WITH_TAG;
}
}
else if (options.types || options.initiated || options.counterparty) {
if (options.limit > MAX_LIMIT_WITH_FILTER) {
options.limit = MAX_LIMIT_WITH_FILTER;
}
}
}
async function applyStartTxOptions(options) {
if (options.startTxHash) {
const accountTransaction = await Client.getTransaction(options.startTxHash);
if (accountTransaction && !accountTransaction.error) {
options.startTx = {
tx: accountTransaction,
meta: accountTransaction.meta,
};
if (options.forward === true) {
const ledgerIndex = options.startTx.tx.ledger_index;
if (options.ledgerIndexMin === undefined || ledgerIndex > options.ledgerIndexMin) {
options.ledgerIndexMin = ledgerIndex;
}
}
else {
const ledgerIndex = options.startTx.tx.ledger_index;
if (options.ledgerIndexMax === undefined || ledgerIndex < options.ledgerIndexMax) {
options.ledgerIndexMax = ledgerIndex;
}
}
}
}
}
function filterHelperStartTx(options, transaction) {
return (!options.startTx ||
(options.forward === true
? (0, utils_1.compareTransactions)(transaction, options.startTx) > 0
: (0, utils_1.compareTransactions)(transaction, options.startTx) < 0));
}
function filterHelperTransactions(account, options, transaction) {
if (transaction.validated === false) {
return false;
}
if (options.excludeFailures === true && transaction.meta.TransactionResult !== "tesSUCCESS") {
return false;
}
if (options.types && !options.types.includes(transaction.tx.TransactionType)) {
return false;
}
if (options.initiated === true && transaction.tx.Account !== account) {
return false;
}
if (options.initiated === false && transaction.tx.Account === account) {
return false;
}
if (options.counterparty && !counterpartyFilter(options, transaction)) {
return false;
}
if (typeof options.sourceTag === "number" && transaction.tx.SourceTag !== options.sourceTag) {
return false;
}
if (typeof options.destinationTag === "number" && transaction.tx.DestinationTag !== options.destinationTag) {
return false;
}
return true;
}
function counterpartyFilter(options, transaction) {
if (transaction.tx.Account === options.counterparty) {
return true;
}
if (transaction.tx.Destination === options.counterparty) {
return true;
}
if (transaction.tx.Delegate === options.counterparty) {
return true;
}
if (transaction.tx.Subject === options.counterparty) {
return true;
}
if (transaction.tx.Amount?.issuer === options.counterparty) {
return true;
}
if (transaction.tx.SendMax?.issuer === options.counterparty) {
return true;
}
if (typeof transaction.tx.TakerGets === "object" && transaction.tx.TakerGets?.issuer === options.counterparty) {
return true;
}
if (typeof transaction.tx.TakerPays === "object" && transaction.tx.TakerPays?.issuer === options.counterparty) {
return true;
}
if (transaction.tx.LimitAmount?.issuer === options.counterparty) {
return true;
}
if (transaction.tx.Issuer === options.counterparty) {
return true;
}
if (transaction.tx.NFTokenMinter === options.counterparty) {
return true;
}
return false;
}