Skip to content

Commit

Permalink
Merge branch 'master' into governance-vue
Browse files Browse the repository at this point in the history
  • Loading branch information
JSKitty authored Nov 21, 2024
2 parents 78fc1ea + 60399c6 commit 7dce9a2
Show file tree
Hide file tree
Showing 9 changed files with 643 additions and 289 deletions.
220 changes: 110 additions & 110 deletions cypress/snapshots/html/snapshot.html

Large diffs are not rendered by default.

150 changes: 101 additions & 49 deletions scripts/dashboard/Activity.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { Database } from '../database.js';
import { HistoricalTx, HistoricalTxType } from '../historical_tx.js';
import { getNameOrAddress } from '../contacts-book.js';
import { getEventEmitter } from '../event_bus';
import { beautifyNumber } from '../misc';
import iCheck from '../../assets/icons/icon-check.svg';
import iHourglass from '../../assets/icons/icon-hourglass.svg';
import { blockCount } from '../global.js';
import { beautifyNumber } from '../misc.js';
const props = defineProps({
title: String,
Expand Down Expand Up @@ -69,6 +69,39 @@ const txMap = computed(() => {
};
});
/**
* Returns the information that we need to show (icon + label + amount) for a self transaction
* @param {number} amount - The net amount of transparent PIVs in a transaction
* @param {number} shieldAmount - The net amount of shielded PIVs in a transaction
*/
function txSelfMap(amount, shieldAmount) {
if (shieldAmount == 0 || amount == 0) {
return {
icon: 'fa-recycle',
colour: 'white',
content:
shieldAmount == 0
? translation.activitySentTo
: 'Shield sent to self',
amount: Math.abs(shieldAmount + amount),
};
} else if (shieldAmount > 0) {
return {
icon: 'fa-shield',
colour: 'white',
content: 'Shielding',
amount: shieldAmount,
};
} else if (shieldAmount < 0) {
return {
icon: 'fa-shield',
colour: 'white',
content: 'De-Shielding',
amount: amount,
};
}
}
function updateReward() {
if (!props.rewards) return;
let res = 0;
Expand All @@ -78,6 +111,7 @@ function updateReward() {
}
rewardAmount.value = res;
}
async function update(txToAdd = 0) {
// Return if wallet is not synced yet
if (!wallet.isSynced) {
Expand Down Expand Up @@ -125,30 +159,51 @@ async function parseTXs(arrTXs) {
const newTxs = [];
// Prepare time formatting
const dateOptions = {
year: '2-digit',
month: '2-digit',
day: '2-digit',
};
const timeOptions = {
hour: '2-digit',
minute: '2-digit',
hour12: true,
};
const dateOptions = {
month: 'short',
day: 'numeric',
};
const yearOptions = {
month: 'short',
day: 'numeric',
year: '2-digit',
};
const cDB = await Database.getInstance();
const cAccount = await cDB.getAccount();
const cDate = new Date();
for (const cTx of arrTXs) {
const dateTime = new Date(cTx.time * 1000);
// If this Tx is older than 24h, then hit the `Date` cache logic, otherwise, use a `Time` and skip it
let strDate =
Date.now() / 1000 - cTx.time > 86400
? dateTime.toLocaleDateString(undefined, dateOptions)
: dateTime.toLocaleTimeString(undefined, timeOptions);
if (cTx.blockHeight === -1) {
strDate = 'Pending';
const cTxDate = new Date(cTx.time * 1000);
// Unconfirmed Txs are simply 'Pending'
let strDate = 'Pending';
if (cTx.blockHeight !== -1) {
// Check if it was today (same day, month and year)
const fToday =
cTxDate.getDate() === cDate.getDate() &&
cTxDate.getMonth() === cDate.getMonth() &&
cTxDate.getFullYear() === cDate.getFullYear();
// Figure out the most convenient time display for this Tx
if (fToday) {
// TXs made today are displayed by time (02:13 pm)
strDate = cTxDate.toLocaleTimeString(undefined, timeOptions);
} else if (cTxDate.getFullYear() === cDate.getFullYear()) {
// TXs older than today are displayed by short date (18 Nov)
strDate = cTxDate.toLocaleDateString(undefined, dateOptions);
} else {
// TXs in previous years are displayed by their short date and year (18 Nov 2023)
strDate = cTxDate.toLocaleDateString(undefined, yearOptions);
}
}
let amountToShow = Math.abs(cTx.amount + cTx.shieldAmount);
// Coinbase Transactions (rewards) require coinbaseMaturity confs
let fConfirmed =
cTx.blockHeight > 0 &&
Expand All @@ -157,54 +212,30 @@ async function parseTXs(arrTXs) {
? cChainParams.current.coinbaseMaturity
: 6);
// Choose the content type, for the Dashboard; use a generative description, otherwise, a TX-ID
// let txContent = props.rewards ? cTx.id : 'Block Reward';
// Format the amount to reduce text size
let formattedAmt = '';
if (cTx.amount < 0.01) {
formattedAmt = beautifyNumber('0.01', '13px');
} else if (cTx.amount >= 100) {
formattedAmt = beautifyNumber(
Math.round(cTx.amount).toString(),
'13px'
);
} else {
formattedAmt = beautifyNumber(`${cTx.amount.toFixed(2)}`, '13px');
}
// For 'Send' TXs: Check if this is a send-to-self transaction
let fSendToSelf = false;
if (cTx.type === HistoricalTxType.SENT) {
fSendToSelf = true;
// Check all addresses to find our own, caching them for performance
for (const strAddr of cTx.receivers) {
// If a previous Tx checked this address, skip it, otherwise, check it against our own address(es)
if (!wallet.isOwnAddress(strAddr)) {
// External address, this is not a self-only Tx
fSendToSelf = false;
}
}
}
// Take the icon, colour and content based on the type of the transaction
let { icon, colour, content } = txMap.value[cTx.type];
const match = content.match(/{(.)}/);
if (match) {
let who = '';
if (fSendToSelf) {
if (cTx.isToSelf && cTx.type !== HistoricalTxType.DELEGATION) {
who = translation.activitySelf;
} else if (cTx.shieldedOutputs) {
who = translation.activityShieldedAddress;
const descriptor = txSelfMap(cTx.amount, cTx.shieldAmount);
icon = descriptor.icon;
colour = descriptor.colour;
content = descriptor.content;
amountToShow = descriptor.amount;
} else {
const arrAddresses = cTx.receivers
let arrAddresses = cTx.receivers
.map((addr) => [wallet.isOwnAddress(addr), addr])
.filter(([isOwnAddress, _]) => {
return cTx.type === HistoricalTxType.RECEIVED
? isOwnAddress
: !isOwnAddress;
})
.map(([_, addr]) => getNameOrAddress(cAccount, addr));
if (cTx.type == HistoricalTxType.RECEIVED) {
arrAddresses = arrAddresses.concat(cTx.shieldReceivers);
}
who =
[
...new Set(
Expand All @@ -215,16 +246,37 @@ async function parseTXs(arrTXs) {
)
),
].join(', ') + '...';
if (
cTx.type == HistoricalTxType.SENT &&
arrAddresses.length == 0
) {
// We sent a shield note to someone, but we cannot decrypt the recipient
// So show a generic "Sent to shield address"
who = translation.activityShieldedAddress;
}
}
content = content.replace(/{.}/, who);
}
// Format the amount to reduce text size
let formattedAmt = '';
if (amountToShow < 0.01) {
formattedAmt = beautifyNumber('0.01', '13px');
} else if (amountToShow >= 100) {
formattedAmt = beautifyNumber(
Math.round(amountToShow).toString(),
'13px'
);
} else {
formattedAmt = beautifyNumber(`${amountToShow.toFixed(2)}`, '13px');
}
newTxs.push({
date: strDate,
id: cTx.id,
content: props.rewards ? cTx.id : content,
formattedAmt,
amount: cTx.amount,
amount: amountToShow,
confirmed: fConfirmed,
icon,
colour,
Expand Down
17 changes: 12 additions & 5 deletions scripts/historical_tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,34 @@ export class HistoricalTx {
* @param {HistoricalTxType} type - The type of transaction.
* @param {string} id - The transaction ID.
* @param {Array<string>} receivers - The list of 'output addresses'.
* @param {boolean} shieldedOutputs - If this transaction contains Shield outputs.
* @param {Array<string>} shieldReceivers - The list of decrypted 'shield output addresses'.
* @param {number} time - The block time of the transaction.
* @param {number} blockHeight - The block height of the transaction.
* @param {number} amount - The amount transacted, in coins.
* @param {number} amount - The transparent amount transacted, in coins.
* @param {number} shieldAmount - The shielded amount transacted, in coins.
* @param {boolean} isToSelf - If the transaction is to self.
* @param {boolean} isConfirmed - Whether the transaction has been confirmed.
*/
constructor(
type,
id,
receivers,
shieldedOutputs,
shieldReceivers,
time,
blockHeight,
amount
amount,
shieldAmount,
isToSelf
) {
this.type = type;
this.id = id;
this.receivers = receivers;
this.shieldedOutputs = shieldedOutputs;
this.shieldReceivers = shieldReceivers;
this.time = time;
this.blockHeight = blockHeight;
this.amount = amount;
this.shieldAmount = shieldAmount;
this.isToSelf = isToSelf;
}
}

Expand Down
38 changes: 22 additions & 16 deletions scripts/mempool.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,15 @@ export class Mempool {
* @param {import('./transaction.js').Transaction} tx
*/
getDebit(tx) {
return tx.vin
.filter(
(input) =>
this.getOutpointStatus(input.outpoint) & OutpointState.OURS
)
const filteredVin = tx.vin.filter(
(input) =>
this.getOutpointStatus(input.outpoint) & OutpointState.OURS
);
const debit = filteredVin
.map((i) => this.outpointToUTXO(i.outpoint))
.reduce((acc, u) => acc + (u?.value || 0), 0);
const ownAllVin = tx.vin.length === filteredVin.length;
return { debit, ownAllVin };
}

/**
Expand All @@ -133,17 +135,21 @@ export class Mempool {
getCredit(tx) {
const txid = tx.txid;

return tx.vout
.filter(
(_, i) =>
this.getOutpointStatus(
new COutpoint({
txid,
n: i,
})
) & OutpointState.OURS
)
.reduce((acc, u) => acc + u?.value ?? 0, 0);
const filteredVout = tx.vout.filter(
(_, i) =>
this.getOutpointStatus(
new COutpoint({
txid,
n: i,
})
) & OutpointState.OURS
);
const credit = filteredVout.reduce((acc, u) => acc + u?.value ?? 0, 0);
const ownAllVout = tx.vout.length === filteredVout.length;
return {
credit,
ownAllVout,
};
}

/**
Expand Down
Loading

0 comments on commit 7dce9a2

Please sign in to comment.