UNPKG

vuex-bitshares

Version:
290 lines (254 loc) 10 kB
import { Aes, TransactionHelper, PrivateKey, ops } from 'bitsharesjs'; /** * Return object with keys = id of each element of array (element.id) * @param {Array} array - array of data elements */ export const arrayToObject = (array) => { const obj = {}; array.forEach(item => { obj[item.id] = item; }); return obj; }; /** * Returns array containing first and last history prices of asset. * @param {Array} history - array with asset's history data */ export const getPrices = (history) => { if (!history.length) return { first: 0, last: 0 }; const startElem = history[0]; const endElem = history[history.length - 1]; // || 1 when node sends bad data ( 0 ) const startPrice = (startElem.open_base || 1) / (startElem.open_quote || 1); const endPrice = (endElem.close_base || 1) / (endElem.close_quote || 1); if (!startElem.open_base || !startElem.close_base || !startElem.open_quote || !endElem.close_quote) { console.warn('[MARKET] : bad price history'); } return { first: startPrice, last: endPrice }; }; /** * Returns formatted prices for array calculated taking precision щf assets into account * @param {Object} prices - object with asset's history prices * @param {number} prices.first - first price of asset history * @param {number} prices.last - last price of asset history (current) * @param {Object} base - base asset object * @param {Object} quote - quote asset object */ export const formatPrices = (prices, base, quote) => { const precisionDiff = base.precision - quote.precision; if (precisionDiff > 0) { prices.first /= (precisionDiff * 10); prices.last /= (precisionDiff * 10); } else if (precisionDiff < 0) { prices.first = prices.first * 10 * precisionDiff; prices.last = prices.last * 10 * precisionDiff; } prices.change = Math.floor(((prices.last / prices.first) * 100) - 100); prices.first = Math.abs(prices.first); prices.last = Math.abs(prices.last); return prices; }; /** * Returns amount of change by percent, calculated by prices history and exchange multiplier * @param {Object} object.prices - object with asset's history prices * @param {number} object.prices.first - first price of asset history * @param {number} object.prices.last - last price of asset history (current) * @param {Object} object.multiplier - object with base -> fiat exchange rates * @param {number} object.multiplier.first - multiplier for first history price * @param {number} object.multiplier.last - multiplier for last history price (current) */ export const calcPercentChange = (prices, multiplier) => { // temporary fix, remove after new portfolio with separate market and history modules implemented if (prices.first === 0) return 0; return ((((prices.last * multiplier.last) / (prices.first * multiplier.first)) * 100) - 100); }; /** * Returns object with balance in base currency, balance in fiat currency and change by percent * @param {Object} object - object containing data for calculation * @param {number} object.balance - balance of asset * @param {Object} object.assetPrices - object with asset's history prices * @param {number} object.assetPrices.first - first price of asset history * @param {number} object.assetPrices.last - last price of asset history (current) * @param {Object} object.fiatMultiplier - object with base -> fiat exchange rates * @param {number} object.fiatMultiplier.first - multiplier for first history price * @param {number} object.fiatMultiplier.last - multiplier for last history price (current) * @param {Boolean} object.isBase - the asset for calculation is base asset * @param {Boolean} object.isFiat - the asset for calculation is fiat asset */ export const calcPortfolioData = ({ balance, assetPrices, fiatMultiplier, isBase, isFiat }) => { let multiplier = fiatMultiplier; let prices = assetPrices; if (isFiat) multiplier = { first: 1, last: 1 }; if (isBase) prices = { first: 1, last: 1 }; const balanceBase = balance * prices.last; const balanceFiat = balanceBase * multiplier.last; let change = calcPercentChange(prices, multiplier); if (prices.last === prices.first && !isBase) change = 0; return { balanceBase, balanceFiat, change }; }; export const encryptMemo = (memo, fromKey, toPubkey) => { const nonce = TransactionHelper.unique_nonce_uint64(); const activePubkey = fromKey.toPublicKey().toPublicKeyString(); const message = Aes.encrypt_with_checksum( fromKey, toPubkey, nonce, memo ); return { from: activePubkey, to: toPubkey, nonce, message }; }; export const decryptMemo = (memo, privateKey) => { return Aes.decrypt_with_checksum( privateKey, memo.from, memo.nonce, memo.message ).toString('utf-8'); }; export const getMemoPrivKey = (keys, publicKey) => { const { active, owner } = keys; const ownerPubkey = owner.toPublicKey().toPublicKeyString(); const activePubkey = active.toPublicKey().toPublicKeyString(); if (publicKey === ownerPubkey) { return owner; } if (publicKey === activePubkey) { return active; } return false; }; export const getMemoSize = (memo) => { const privKey = '5KikQ23YhcM7jdfHbFBQg1G7Do5y6SgD9sdBZq7BqQWXmNH7gqo'; const memoToKey = 'BTS8eLeqSZZtB1YHdw7KjQxRSRmaKAseCxhUSqaLxUdqvdGpp6nck'; const pKey = PrivateKey.fromWif(privKey); const encrypted = encryptMemo(memo, pKey, memoToKey); const serialized = ops.memo_data.fromObject(encrypted); const stringified = JSON.stringify(ops.memo_data.toHex(serialized)); const byteLength = Buffer.byteLength(stringified, 'hex'); return byteLength; }; /** Calculates distribution 0..1 of total amount of assets expressed * in base asset * @param {Object} balances - {assetId: baseAssetValue} */ export const distributionFromBalances = (balances) => { const total = Object.keys(balances).reduce((res, key) => res + balances[key], 0); return Object.keys(balances).reduce( (res, key) => Object.assign(res, { [key]: balances[key] / total }), {} ); }; /** Corrects distributions to values multiple to 10**accuracy, * keeps summary proportions value equal to 1 * @param {Object} proportions - {assetId: rawProportionValue} * @param {number} accuracy - number of digits after point */ export const distributionSampling = (proportions, accuracy) => { const positiveExponent = `e+${accuracy}`; const negativeExponent = `e-${accuracy}`; const distributionInfo = Object.keys(proportions).map(key => { // proportions rounded dawnward to nearest miltiple of 10 ** accuracy const integer = Math.floor(proportions[key].toFixed(accuracy + 1) + positiveExponent); const floored = +(integer + negativeExponent); const remainder = proportions[key] - floored; return ({ floored, remainder, assetId: key }); }); // summary proporions correction error const correctionError = distributionInfo .reduce((res, { floored }) => res - floored, 1); const proportionsToCorrect = Math.round(correctionError + positiveExponent); // if summary error greater than 10**accuracy, // then correct top n proportions sorted by remainder descending, // where n = error/10**accuracy distributionInfo .sort((a, b) => b.remainder - a.remainder) .slice(0, proportionsToCorrect) .forEach(({ assetId }) => { const index = distributionInfo.findIndex(info => assetId === info.assetId); const integerValue = +(distributionInfo[index].floored + positiveExponent); const correctedInteger = Math.floor(integerValue + 1); distributionInfo[index].floored = +(correctedInteger + negativeExponent); }); return distributionInfo.reduce( (r, { floored, assetId }) => Object.assign(r, { [assetId]: floored }), {} ); }; export const createOrder = ({ sell, receive, userId, fillOrKill = false }) => { const expiration = new Date(); expiration.setYear(expiration.getFullYear() + 5); return { seller: userId, amount_to_sell: sell, min_to_receive: receive, expiration, fill_or_kill: fillOrKill }; }; export const getValuesToUpdate = (balances, baseBalances, update) => { const totalBase = Object.keys(baseBalances).reduce((res, key) => res + baseBalances[key], 0); const distribution = distributionFromBalances(baseBalances); const result = { sell: {}, buy: {} }; Object.keys(update).forEach((assetId) => { if (assetId === '1.3.0') return; const futureShare = update[assetId]; const currentShare = distribution[assetId]; console.log('CALC VALUES:', assetId); console.log(futureShare); console.log(currentShare); if (futureShare === 0) { result.sell[assetId] = balances[assetId]; } else if (futureShare > currentShare) { const futureBase = Math.floor(totalBase * futureShare); const currentBase = Math.floor(totalBase * currentShare); result.buy[assetId] = futureBase - currentBase; } else { const fullAmmountInCurrent = Math.floor(balances[assetId] / currentShare); const amountInFuture = Math.floor(fullAmmountInCurrent * futureShare); const otherPortfolioAmount = fullAmmountInCurrent - balances[assetId]; const amountToSell = fullAmmountInCurrent - otherPortfolioAmount - amountInFuture; result.sell[assetId] = amountToSell; } }); return result; }; export const calcPortfolioItem = ({ asset, prices, baseAsset, fiatMultiplier, balance }) => { const multiplier = fiatMultiplier; const baseValue = parseInt((balance * prices.last).toFixed(0), 10); const baseValuePrecised = baseValue / (10 ** baseAsset.precision); const fiatValue = parseInt((baseValue * fiatMultiplier.last).toFixed(0), 10); let change = calcPercentChange(prices, multiplier); if (prices.fist === prices.last && asset.id !== baseAsset.id) change = 0; return { name: asset.symbol, balance, baseValue, baseValuePrecised, basePrecision: baseAsset.precision, fiatValue, change }; };