Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[explorer/frontend] feat: add fees breakdown section to transaction page #1227

Open
wants to merge 2 commits into
base: explorer-frontend/dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 106 additions & 5 deletions explorer/frontend/__tests__/test-utils/transactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,11 @@ export const transactionPageResponse = [
deadline: '2024-03-29 14:06:21',
embeddedTransactions: [
{
fee: 0.13,
initiator: 'ND2RSOCXYGMABR6LESAW5ENC22Z34G3IL63ABALN',
signatures: [
{
fee: 0.17,
signature:
'7D79A1ECFC4BE173BFFF9D54DCD0B0DED43C8F56E875E6BEF1E9EFFC32091E98A3BF0D7C4F66BDA9874A3120D1CB233C8D3A9EB963946100D776C8181F07EF01',
signer: 'NDIVJFQIJR3VWXJRDSNFK6U37HM4DZNORXI3IRTV'
Expand All @@ -179,6 +181,7 @@ export const transactionPageResponse = [
deadline: '2024-03-29 14:06:29',
embeddedTransactions: [
{
fee: 0.13,
initiator: 'ND2RSOCXYGMABR6LESAW5ENC22Z34G3IL63ABALN',
message: {
is_plain: 1,
Expand All @@ -190,6 +193,7 @@ export const transactionPageResponse = [
},
signatures: [
{
fee: 0.17,
signature:
'7D79A1ECFC4BE173BFFF9D54DCD0B0DED43C8F56E875E6BEF1E9EFFC32091E98A3BF0D7C4F66BDA9874A3120D1CB233C8D3A9EB963946100D776C8181F07EF01',
signer: 'NDIVJFQIJR3VWXJRDSNFK6U37HM4DZNORXI3IRTV'
Expand Down Expand Up @@ -746,7 +750,7 @@ export const transactionPageResult = {

signature:
'7A4F8F82B6A144E568F5B33DE055D35E9F8D3EE2FC200F0DE9D2571DF5957FF942B8E5A99ED1726D3F459218CF9F27EA3F264011B04122BB9E3E391E72EE930D',
fee: 0.15,
fee: 0.45,
value: [
{
id: 'nem.xem',
Expand All @@ -763,10 +767,29 @@ export const transactionPageResult = {
],
signatures: [
{
fee: 0.17,
signature:
'7D79A1ECFC4BE173BFFF9D54DCD0B0DED43C8F56E875E6BEF1E9EFFC32091E98A3BF0D7C4F66BDA9874A3120D1CB233C8D3A9EB963946100D776C8181F07EF01',
signer: 'NDIVJFQIJR3VWXJRDSNFK6U37HM4DZNORXI3IRTV'
}
],
feesBreakdown: [
{
type: 'multisigFee',
amount: 0.15
},
{
type: 'embeddedTransactionsFee',
amount: 0.13
},
{
type: 'signaturesFee',
amount: 0.17
},
{
type: 'totalFee',
amount: 0.45
}
]
},
{
Expand All @@ -785,7 +808,7 @@ export const transactionPageResult = {

signature:
'7A4F8F82B6A144E568F5B33DE055D35E9F8D3EE2FC200F0DE9D2571DF5957FF942B8E5A99ED1726D3F459218CF9F27EA3F264011B04122BB9E3E391E72EE930D',
fee: 0.15,
fee: 0.45,
amount: 13200,
value: [
{
Expand Down Expand Up @@ -814,10 +837,29 @@ export const transactionPageResult = {
],
signatures: [
{
fee: 0.17,
signature:
'7D79A1ECFC4BE173BFFF9D54DCD0B0DED43C8F56E875E6BEF1E9EFFC32091E98A3BF0D7C4F66BDA9874A3120D1CB233C8D3A9EB963946100D776C8181F07EF01',
signer: 'NDIVJFQIJR3VWXJRDSNFK6U37HM4DZNORXI3IRTV'
}
],
feesBreakdown: [
{
type: 'multisigFee',
amount: 0.15
},
{
type: 'embeddedTransactionsFee',
amount: 0.13
},
{
type: 'signaturesFee',
amount: 0.17
},
{
type: 'totalFee',
amount: 0.45
}
]
},
{
Expand Down Expand Up @@ -1612,7 +1654,7 @@ export const transactionAccountPageResult = {
height: 4694401,
signature:
'7A4F8F82B6A144E568F5B33DE055D35E9F8D3EE2FC200F0DE9D2571DF5957FF942B8E5A99ED1726D3F459218CF9F27EA3F264011B04122BB9E3E391E72EE930D',
fee: 0.15,
fee: 0.45,
value: [
{
id: 'nem.xem',
Expand All @@ -1629,10 +1671,29 @@ export const transactionAccountPageResult = {
],
signatures: [
{
fee: 0.17,
signature:
'7D79A1ECFC4BE173BFFF9D54DCD0B0DED43C8F56E875E6BEF1E9EFFC32091E98A3BF0D7C4F66BDA9874A3120D1CB233C8D3A9EB963946100D776C8181F07EF01',
signer: 'NDIVJFQIJR3VWXJRDSNFK6U37HM4DZNORXI3IRTV'
}
],
feesBreakdown: [
{
type: 'multisigFee',
amount: 0.15
},
{
type: 'embeddedTransactionsFee',
amount: 0.13
},
{
type: 'signaturesFee',
amount: 0.17
},
{
type: 'totalFee',
amount: 0.45
}
]
},
{
Expand All @@ -1649,7 +1710,7 @@ export const transactionAccountPageResult = {
height: 4694401,
signature:
'7A4F8F82B6A144E568F5B33DE055D35E9F8D3EE2FC200F0DE9D2571DF5957FF942B8E5A99ED1726D3F459218CF9F27EA3F264011B04122BB9E3E391E72EE930D',
fee: 0.15,
fee: 0.45,
amount: 13200,
value: [
{
Expand Down Expand Up @@ -1678,10 +1739,29 @@ export const transactionAccountPageResult = {
],
signatures: [
{
fee: 0.17,
signature:
'7D79A1ECFC4BE173BFFF9D54DCD0B0DED43C8F56E875E6BEF1E9EFFC32091E98A3BF0D7C4F66BDA9874A3120D1CB233C8D3A9EB963946100D776C8181F07EF01',
signer: 'NDIVJFQIJR3VWXJRDSNFK6U37HM4DZNORXI3IRTV'
}
],
feesBreakdown: [
{
type: 'multisigFee',
amount: 0.15
},
{
type: 'embeddedTransactionsFee',
amount: 0.13
},
{
type: 'signaturesFee',
amount: 0.17
},
{
type: 'totalFee',
amount: 0.45
}
]
},
{
Expand Down Expand Up @@ -2205,6 +2285,7 @@ export const transactionInfoResponse = {
deadline: '2024-03-29 19:13:31',
embeddedTransactions: [
{
fee: 0.13,
initiator: 'NANEWEN4LPV4CSDLFW3OPFUC57LQQDEFY5QDLHAG',
message: {
is_plain: 1,
Expand All @@ -2216,6 +2297,7 @@ export const transactionInfoResponse = {
},
signatures: [
{
fee: 0.17,
signature:
'8869F54E0B9CF93BFE2811982428799428656B3A18D73EB9B56C68D7FA64D5403860DA00EEF9EF3E63E0C6331D8CCF85078225E9888EEA50520A640C4A102709',
signer: 'NCQME4TROE2LM2SE53WYZMECOQZEPPKMQ27TZEN2'
Expand Down Expand Up @@ -2252,7 +2334,7 @@ export const transactionInfoResult = {

signature:
'84FC7751D87ED49EACDA60EE2D5F4B5F8020A32D7593A2E268054BCCC919FCD6DB4F66B35B60E1E0DF8E229B9510090C6A203D7426A7E6F595AEABE98EA3820F',
fee: 0.15,
fee: 0.45,
amount: 7,
value: [
{
Expand Down Expand Up @@ -2281,11 +2363,30 @@ export const transactionInfoResult = {
],
signatures: [
{
fee: 0.17,
signature:
'8869F54E0B9CF93BFE2811982428799428656B3A18D73EB9B56C68D7FA64D5403860DA00EEF9EF3E63E0C6331D8CCF85078225E9888EEA50520A640C4A102709',
signer: 'NCQME4TROE2LM2SE53WYZMECOQZEPPKMQ27TZEN2'
}
],
feesBreakdown: [
{
type: 'multisigFee',
amount: 0.15
},
{
type: 'embeddedTransactionsFee',
amount: 0.13
},
{
type: 'signaturesFee',
amount: 0.17
},
{
type: 'totalFee',
amount: 0.45
}
],
accountStateChange: [
{
address: 'NB2CCC4ZKHX32CF4QU4X22S6LOS765W5A6LDKLJ2',
Expand Down
10 changes: 5 additions & 5 deletions explorer/frontend/__tests__/utils/server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ describe('utils/server', () => {
it('throws error if function throws error with no 404 status', async () => {
// Arrange:
const error = {
status: 502
};
const fetchFunction = jest.fn().mockRejectedValue({
response: {
data: error
data: {
status: 502
}
}
});
};
const fetchFunction = jest.fn().mockRejectedValue(error);
const wrappedFetchFunction = createTryFetchInfoFunction(fetchFunction);

// Act:
Expand Down
33 changes: 29 additions & 4 deletions explorer/frontend/api/transactions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import config from '@/config';
import { ACCOUNT_STATE_CHANGE_ACTION, COSIGNATORY_MODIFICATION_ACTION, TRANSACTION_DIRECTION, TRANSACTION_TYPE } from '@/constants';
import { decodeTransactionMessage } from '@/utils/common';
import { decodeTransactionMessage, truncateDecimals } from '@/utils/common';
import { createAPIURL, createPage, createSearchCriteria, createSearchURL, createTryFetchInfoFunction, makeRequest } from '@/utils/server';

/**
Expand Down Expand Up @@ -324,21 +324,46 @@ const formatAccountKeyLink = (data, filter) => {
const formatMultisigTransaction = (data, filter) => {
const rawEmbeddedTransaction = {
...data,
transactionType: data.embeddedTransactions[0].transactionType
transactionType: data.embeddedTransactions[0].transactionType,
fee: data.embeddedTransactions[0].fee
};
if (rawEmbeddedTransaction.transactionType === TRANSACTION_TYPE.TRANSFER)
rawEmbeddedTransaction.value = [{ message: data.embeddedTransactions[0].message }, data.embeddedTransactions[0].mosaics];
else
rawEmbeddedTransaction.value = data.embeddedTransactions;

const formattedTransaction = formatBaseTransaction(data, filter);
const formattedEmbeddedTransaction = transactionFromDTO(rawEmbeddedTransaction, filter.address);

// Fees breakdown
const multisigFee = truncateDecimals(formattedTransaction.fee, config.NATIVE_MOSAIC_DIVISIBILITY);
const embeddedTransactionsFee = truncateDecimals(formattedEmbeddedTransaction.fee, config.NATIVE_MOSAIC_DIVISIBILITY);
const signaturesFee = truncateDecimals(
data.embeddedTransactions[0].signatures.reduce((total, signature) => total + (signature.fee || 0), 0),
config.NATIVE_MOSAIC_DIVISIBILITY
);
const feesBreakdownData = {
multisigFee,
embeddedTransactionsFee,
signaturesFee,
totalFee: truncateDecimals(
(signaturesFee + multisigFee + embeddedTransactionsFee).toPrecision(config.NATIVE_MOSAIC_DIVISIBILITY),
config.NATIVE_MOSAIC_DIVISIBILITY
)
};
const feesBreakdown = Object.entries(feesBreakdownData).map(([key, value]) => ({
type: key,
amount: value
}));

return {
...formatBaseTransaction(data, filter),
...formattedTransaction,
signatures: data.embeddedTransactions[0].signatures,
signer: data.embeddedTransactions[0].initiator,
amount: formattedEmbeddedTransaction.amount,
value: formattedEmbeddedTransaction.value,
body: formattedEmbeddedTransaction.body
body: formattedEmbeddedTransaction.body,
fee: feesBreakdownData.totalFee,
feesBreakdown
};
};
24 changes: 15 additions & 9 deletions explorer/frontend/components/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@ const Table = ({
isLoading,
isError,
isLastPage,
isLastColumnAligned
isLastColumnAligned,
isHeaderHidden,
isColumnsStacked
}) => {
const { t } = useTranslation('common');
const isMobile = useMediaQuery('(max-width: 767.8px)');
const isMobileTableVisible = isMobile && !!renderItemMobile;
const isDesktopTableVisible = !isMobile || !isMobileTableVisible;
const desktopTableStyle = !renderItemMobile ? styles.dataMobile : '';
const headerRowStyle = `${styles.header} ${isColumnsStacked ? styles.header_stacked : ''}`;
const headerCellStyle = `${styles.headerCell} ${isLastColumnAligned && styles.headerCell_aligned}`;
const dataRowStyle = `${styles.dataRow} ${isColumnsStacked ? styles.dataRow_stacked : ''}`;
const dataCellStyle = isLastColumnAligned ? styles.dataCell_aligned : '';
const isEmptyTableMessageShown = !isLoading && ((!!data && !data.length) || (!!sections && !sections.length));

const renderRow = (row, index) => (
<div className={styles.dataRow} key={'tr' + index}>
<div className={dataRowStyle} key={'tr' + index}>
{columns.map((item, index) => (
<div className={dataCellStyle} style={{ width: item.size }} key={'td' + index}>
{item.renderValue ? item.renderValue(row[item.key], row) : row[item.key]}
Expand All @@ -42,13 +46,15 @@ const Table = ({

return (
<div className={styles.table}>
<div className={styles.header}>
{columns.map((item, index) => (
<div className={headerCellStyle} style={{ width: item.size }} key={'th' + index}>
{item.renderTitle ? item.renderTitle(item.key) : t(`table_field_${item.key}`)}
</div>
))}
</div>
{!isHeaderHidden && (
<div className={headerRowStyle}>
{columns.map((item, index) => (
<div className={headerCellStyle} style={{ width: item.size }} key={'th' + index}>
{item.renderTitle ? item.renderTitle(item.key) : t(`table_field_${item.key}`)}
</div>
))}
</div>
)}
{!!data && isDesktopTableVisible && <div className={`${styles.data} ${desktopTableStyle}`}>{data.map(renderRow)}</div>}
{!!data && isMobileTableVisible && <div className={styles.listMobile}>{data.map(renderMobileListItem)}</div>}
{isEmptyTableMessageShown && <div className={styles.emptyListMessage}>{t('message_emptyTable')}</div>}
Expand Down
Loading