UNPKG

@xmr-core/xmr-mymonero-libs

Version:

A collection of utilities for building ontop of a MyMonero compatible API

202 lines (173 loc) 4.82 kB
import { AddressTransactions, AddressTransactionsTx, NormalizedTransaction, SpentOutput, AddressInfo, } from "./types"; import { Omit } from "../../types"; import { BigInt } from "@xmr-core/biginteger"; import { formatMoney } from "@xmr-core/xmr-money"; import { Output } from "@xmr-core/xmr-transaction"; export function isKeyImageEqual({ key_image }: SpentOutput, keyImage: string) { return key_image === keyImage; } //#region addressInfo export function normalizeAddressInfo(data: AddressInfo): Required<AddressInfo> { data.spent_outputs = data.spent_outputs || []; const defaultObj: Required<AddressInfo> = { total_sent: "0", total_received: "0", locked_funds: "0", start_height: 0, scanned_block_height: 0, scanned_height: 0, transaction_height: 0, blockchain_height: 0, rates: {}, spent_outputs: [] as SpentOutput[], }; return { ...defaultObj, ...data }; } //#endregion addressInfo //#region parseAddressTransactions export function normalizeAddressTransactions( data: AddressTransactions, ): Required<AddressTransactions> { data.transactions = data.transactions || []; const defaultObj: Required<AddressTransactions> = { scanned_height: 0, scanned_block_height: 0, start_height: 0, transaction_height: 0, blockchain_height: 0, transactions: [] as AddressTransactionsTx[], total_received: "0", }; return { ...defaultObj, ...data }; } export function normalizeTransaction( tx: AddressTransactionsTx, ): NormalizedTransaction { tx.spent_outputs = tx.spent_outputs || []; const defaultObj: Omit<NormalizedTransaction, "timestamp"> = { amount: new BigInt(0), approx_float_amount: 0, hash: "", height: 0, id: 0, mempool: false, coinbase: false, mixin: 0, spent_outputs: [] as SpentOutput[], total_received: "0", total_sent: new BigInt(0), unlock_time: 0, payment_id: "", }; const mergedObj = { ...defaultObj, ...tx, total_sent: tx.total_sent ? new BigInt(tx.total_sent) : defaultObj.total_sent, timestamp: new Date(tx.timestamp), }; return mergedObj; } /** * * @description Validates that the sum of total received and total sent is greater than 1 * @param {NormalizedTransaction} { total_received, total_sent} */ export function zeroTransactionAmount({ total_received, total_sent, }: NormalizedTransaction) { return new BigInt(total_received).add(total_sent).compare(0) <= 0; } export function calculateTransactionAmount({ total_received, total_sent, }: NormalizedTransaction) { return new BigInt(total_received).subtract(total_sent); } export function estimateTransactionAmount({ amount }: NormalizedTransaction) { return parseFloat(formatMoney(amount)); } /** * * @description If the transaction is: * 1. An outgoing transaction, e.g. it is sending moneroj to an address rather than receiving it * 2. And contains an encrypted (8 byte) payment id * * Then, it is removed from the transaction object, because the server (MyMonero) can't filter out short (encrypted) pids on outgoing txs * @export * @param {NormalizedTransaction} tx */ export function removeEncryptedPaymentIDs(tx: NormalizedTransaction) { const { payment_id, approx_float_amount } = tx; const outgoingTxWithEncPid = payment_id && payment_id.length === 16 && approx_float_amount < 0; if (outgoingTxWithEncPid) { delete tx.payment_id; } } /** * @description Sort transactions based on the following cases where higher priorities * gain a lower index value: * * 1. Transactions that are in the mempool gain priority * 2. Otherwise, sort by id, where the higher ID has higher priority * * @export * @param {AddressTransactionsTx[]} transactions * @returns */ export function sortTransactions(transactions: NormalizedTransaction[]) { return transactions.sort((a, b) => { if (a.mempool && !b.mempool) { return -1; // a first } else if (b.mempool) { return 1; // b first } // both mempool - fall back to .id compare return b.id - a.id; }); } //#endregion parseAddressTransactions //#region parseUnspentOuts export function validateUnspentOutput(out: Output, index: number) { if (!out) { throw Error(`unspent_output at index ${index} was null`); } } export function validateSpendKeyImages(keyImgs: string[], index: number) { if (!keyImgs) { throw Error( `spend_key_images of unspent_output at index ${index} was null`, ); } } export function validUnspentOutput(out: Output, index: number) { if (out) { return true; } else { console.warn( `This unspent output at i ${index} was undefined! Skipping.`, ); return false; } } export function validTxPubKey(out: Output) { if (out.tx_pub_key) { return true; } else { console.warn( "This unspent out was missing a tx_pub_key! Skipping.", out, ); return false; } } //#endregion parseUnspentOuts