155 lines
3.8 KiB
JavaScript
155 lines
3.8 KiB
JavaScript
import { DEFAULT_GOAL_EMOJI } from '../../../../constants';
|
|
|
|
export const toMicros = value => {
|
|
const numeric = Number(value || 0);
|
|
if (!Number.isFinite(numeric) || numeric <= 0) return 0;
|
|
return Math.round(numeric * 1_000_000);
|
|
};
|
|
|
|
export const fromMicros = value => {
|
|
const numeric = Number(value || 0);
|
|
if (!Number.isFinite(numeric)) return 0;
|
|
return (numeric / 1_000_000).toFixed(2);
|
|
};
|
|
|
|
export const formatTxDate = timestamp => {
|
|
const date = new Date(Number(timestamp || Date.now()));
|
|
return date.toLocaleDateString(undefined, {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: 'numeric',
|
|
});
|
|
};
|
|
|
|
export async function fetchSavingsInterestPayouts(
|
|
pubKeyHex,
|
|
limit = 10,
|
|
offset = 0,
|
|
) {
|
|
try {
|
|
const res = await fetch(
|
|
`https://rewards.flashnet.xyz/v1/rewards/${pubKeyHex}/payouts?limit=${limit}&offset=${offset}`,
|
|
);
|
|
if (!res.ok) return [];
|
|
const data = await res.json();
|
|
if (Array.isArray(data)) return data;
|
|
if (Array.isArray(data.payouts)) return data.payouts;
|
|
return [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export function parseGoalMetadata(metadata) {
|
|
if (!metadata) return {};
|
|
try {
|
|
const parsed = JSON.parse(metadata);
|
|
return typeof parsed === 'object' && parsed ? parsed : {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
export function serializeGoalMetadata(metadata) {
|
|
if (!metadata || typeof metadata !== 'object') return null;
|
|
try {
|
|
return JSON.stringify(metadata);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function signedTransactionAmountMicros(transaction) {
|
|
const amount = Math.max(
|
|
0,
|
|
Math.round(Number(transaction?.amountMicros || 0)),
|
|
);
|
|
return transaction?.type === 'withdrawal' ? -amount : amount;
|
|
}
|
|
|
|
export function computeSavingsBalanceMicros(transactions) {
|
|
return transactions.reduce(
|
|
(sum, tx) => sum + signedTransactionAmountMicros(tx),
|
|
0,
|
|
);
|
|
}
|
|
|
|
export function computeGoalBalanceMicros(goalId, transactions) {
|
|
if (!goalId) return 0;
|
|
return transactions
|
|
.filter(tx => tx.goalId === goalId)
|
|
.reduce((sum, tx) => sum + signedTransactionAmountMicros(tx), 0);
|
|
}
|
|
|
|
export function toLegacyDisplayTransaction(transaction, goalName, t) {
|
|
const signedAmountMicros = signedTransactionAmountMicros(transaction);
|
|
const isWithdrawal = transaction.type === 'withdrawal';
|
|
const isBtcWithdrawl = transaction.type === 'bitcoinWithdrawal';
|
|
|
|
const key = isWithdrawal
|
|
? goalName
|
|
? 'savings.withdrawalWithGoal'
|
|
: 'savings.withdrawal'
|
|
: goalName
|
|
? 'savings.depositWithGoal'
|
|
: 'savings.deposit';
|
|
|
|
return {
|
|
txId: transaction.id,
|
|
goalId: transaction.goalId,
|
|
type: transaction.type,
|
|
amountMicros: signedAmountMicros,
|
|
createdAt: transaction.timestamp,
|
|
description: isBtcWithdrawl
|
|
? t('savings.rewardsWithdrawal')
|
|
: t(key, { goalName }),
|
|
};
|
|
}
|
|
|
|
export function normalizeGoalForUI(goal) {
|
|
if (!goal) return null;
|
|
const metadata = parseGoalMetadata(goal.metadata);
|
|
|
|
return {
|
|
...goal,
|
|
amountMicros: goal.targetAmountMicros,
|
|
emoji: metadata.emoji || DEFAULT_GOAL_EMOJI,
|
|
};
|
|
}
|
|
|
|
export function resolveGoalAndAmount(goalIdOrPayload, maybeAmount) {
|
|
if (
|
|
goalIdOrPayload &&
|
|
typeof goalIdOrPayload === 'object' &&
|
|
!Array.isArray(goalIdOrPayload)
|
|
) {
|
|
return {
|
|
goalId: goalIdOrPayload.goalId,
|
|
amount: goalIdOrPayload.amount,
|
|
};
|
|
}
|
|
|
|
return {
|
|
goalId: typeof goalIdOrPayload === 'string' ? goalIdOrPayload : undefined,
|
|
amount: maybeAmount,
|
|
};
|
|
}
|
|
|
|
export function mergeAndSortSavingsActivity(
|
|
contributions = [],
|
|
interestPayouts = [],
|
|
t,
|
|
) {
|
|
const normalised = interestPayouts.map(payout => ({
|
|
txId: payout.txId,
|
|
type: 'interest',
|
|
amountMicros: toMicros(payout.payoutSats),
|
|
createdAt: new Date(payout.createdAt).getTime(),
|
|
description: t('savings.rewardsPayout'),
|
|
}));
|
|
|
|
return [...contributions, ...normalised].sort(
|
|
(a, b) => b.createdAt - a.createdAt,
|
|
);
|
|
}
|