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

feat: link to output when transaction is supply increase #1114

Merged
merged 5 commits into from
Feb 16, 2024
Merged
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
2 changes: 1 addition & 1 deletion api/src/models/db/networkType.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
const LEGACY_MAINNET = "legacy-mainnet";
const CHRYSALIS_MAINNET = "chrysalis-mainnet";
const MAINNET = "mainnet";
export const MAINNET = "mainnet";
const DEVNET = "devnet";
export const SHIMMER = "shimmer";
const TESTNET = "testnet";
Expand Down
51 changes: 45 additions & 6 deletions api/src/routes/stardust/transactionhistory/download/post.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OutputResponse, INodeInfoBaseToken, CommonOutput } from "@iota/sdk";
import { CommonOutput, INodeInfoBaseToken, OutputResponse } from "@iota/sdk";
import JSZip from "jszip";
import moment from "moment";
import { ServiceFactory } from "../../../../factories/serviceFactory";
Expand All @@ -8,6 +8,8 @@ import { ITransactionHistoryDownloadBody } from "../../../../models/api/stardust
import { ITransactionHistoryRequest } from "../../../../models/api/stardust/chronicle/ITransactionHistoryRequest";
import { ITransactionHistoryItem } from "../../../../models/api/stardust/chronicle/ITransactionHistoryResponse";
import { IConfiguration } from "../../../../models/configuration/IConfiguration";
import { INetwork } from "../../../../models/db/INetwork";
import { MAINNET } from "../../../../models/db/networkType";
import { STARDUST } from "../../../../models/db/protocolVersion";
import { NetworkService } from "../../../../services/networkService";
import { ChronicleService } from "../../../../services/stardust/chronicleService";
Expand All @@ -20,13 +22,16 @@ export interface ITransactionHistoryRecord {
isGenesisByDate: boolean;
isSpent: boolean;
transactionId: string;
outputIdFromSupplyIncrease?: string;
timestamp: number;
dateFormatted: string;
balanceChange: number;
balanceChangeFormatted: string;
outputs: OutputWithDetails[];
}

const STARDUST_GENESIS_MILESTONE = 7669900;

/**
* Download the transaction history from chronicle stardust.
* @param _ The configuration.
Expand Down Expand Up @@ -73,14 +78,21 @@ export async function post(
});
const tokenInfo = nodeInfoService.getNodeInfo().baseToken;

const transactions = getTransactionHistoryRecords(groupOutputsByTransactionId(fulfilledOutputs), tokenInfo);
const transactions = getTransactionHistoryRecords(groupOutputsByTransactionId(fulfilledOutputs), tokenInfo, networkConfig);

const headers = ["Timestamp", "TransactionId", "Balance changes"];

let csvContent = `${headers.join(",")}\n`;

for (const transaction of transactions) {
const row = [transaction.dateFormatted, transaction.transactionId, transaction.balanceChangeFormatted].join(",");
let transactionCell = transaction.transactionId;
if (transaction.isGenesisByDate) {
transactionCell = `Stardust Genesis (Chrysalis): ${transactionCell}`;
if (transaction.outputIdFromSupplyIncrease) {
transactionCell = `Supply Increase Output: ${transaction.outputIdFromSupplyIncrease}`;
}
}
const row = [transaction.dateFormatted, transactionCell, transaction.balanceChangeFormatted].join(",");
csvContent += `${row}\n`;
}

Expand Down Expand Up @@ -140,21 +152,26 @@ export const groupOutputsByTransactionId = (outputsWithDetails: OutputWithDetail
export const getTransactionHistoryRecords = (
transactionIdToOutputs: Map<string, OutputWithDetails[]>,
tokenInfo: INodeInfoBaseToken,
networkConfig: INetwork,
): ITransactionHistoryRecord[] => {
const calculatedTransactions: ITransactionHistoryRecord[] = [];

for (const [transactionId, outputs] of transactionIdToOutputs.entries()) {
const lastOutputTime = Math.max(...outputs.map((t) => t.milestoneTimestamp));
const balanceChange = calculateBalanceChange(outputs);

const isGenesisByDate = outputs.map((t) => t.milestoneTimestamp).includes(0);

const isSpent = balanceChange <= 0;
const isGenesisByDate = outputs.some((output) => output.milestoneIndex === STARDUST_GENESIS_MILESTONE);

let outputIdFromSupplyIncrease;
if (isGenesisByDate) {
outputIdFromSupplyIncrease = getStardustGenesisOutputId(outputs, networkConfig, transactionId);
}

calculatedTransactions.push({
isGenesisByDate,
isSpent,
transactionId,
outputIdFromSupplyIncrease,
timestamp: lastOutputTime,
dateFormatted: moment(lastOutputTime * 1000).format("YYYY-MM-DD HH:mm:ss"),
balanceChange,
Expand All @@ -165,6 +182,28 @@ export const getTransactionHistoryRecords = (
return calculatedTransactions;
};

/**
* Get the output id from the stardust genesis.
* @param outputs List of outputs related to transaction
* @param networkConfig Network configuration
* @param transactionId Current trancaction
* @returns The output id from the stardust genesis or undefined.
*/
function getStardustGenesisOutputId(outputs: OutputWithDetails[], networkConfig: INetwork, transactionId: string): string | undefined {
const STARDUST_SUPPLY_INCREASE_OUTPUT_TICKER = "0xb191c4bc825ac6983789e50545d5ef07a1d293a98ad974fc9498cb18";

const outputFromStardustGenesis = outputs.find((output) => {
if (
networkConfig.network === MAINNET &&
output.milestoneIndex === STARDUST_GENESIS_MILESTONE &&
transactionId.includes(STARDUST_SUPPLY_INCREASE_OUTPUT_TICKER)
) {
return true;
}
});
return outputFromStardustGenesis?.outputId;
}

export const calculateBalanceChange = (outputs: OutputWithDetails[]) => {
let totalAmount = 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,7 @@ const TransactionHistory: React.FC<TransactionHistoryProps> = ({ network, addres
<tbody>
{transactions?.map((c, idx) => (
<React.Fragment key={idx}>
<TransactionRow
isGenesisByDate={c.isGenesisByDate}
isTransactionFromStardustGenesis={c.isTransactionFromStardustGenesis}
transactionLink={c.transactionLink}
dateFormatted={c.dateFormatted}
balanceChangeFormatted={c.balanceChangeFormatted}
transactionId={c.transactionId}
isSpent={c.isSpent}
isFormattedAmounts={isFormattedAmounts}
setIsFormattedAmounts={setIsFormattedAmounts}
/>
<TransactionRow {...c} isFormattedAmounts={isFormattedAmounts} setIsFormattedAmounts={setIsFormattedAmounts} />
</React.Fragment>
))}
</tbody>
Expand Down
31 changes: 17 additions & 14 deletions client/src/app/components/stardust/history/TransactionIdView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { STARDUST_SUPPLY_INCREASE_TRANSACTION_ID } from "~/helpers/stardust/transactionsHelper";
import { Link } from "react-router-dom";
import { STARDUST_SUPPLY_INCREASE_OUTPUT_TICKER } from "~/helpers/stardust/transactionsHelper";
import TruncatedId from "../TruncatedId";
import Tooltip from "../../Tooltip";

Expand All @@ -10,21 +11,23 @@ export interface ITransactionIdProps {
}

const TransactionIdView: React.FC<ITransactionIdProps> = ({ transactionId, isTransactionFromStardustGenesis, transactionLink }) => {
if (isTransactionFromStardustGenesis && transactionId.includes(STARDUST_SUPPLY_INCREASE_OUTPUT_TICKER)) {
return (
<Link to={transactionLink}>
<span>Supply Increase</span>
</Link>
);
}

return (
<>
{isTransactionFromStardustGenesis && transactionId.includes(STARDUST_SUPPLY_INCREASE_TRANSACTION_ID) ? (
<span>Stardust Genesis</span>
) : (
<>
<TruncatedId id={transactionId} link={transactionLink} />
{isTransactionFromStardustGenesis && (
<Tooltip tooltipContent="This link opens the transaction on Chrysalis Mainnet" childrenClass="row middle">
<span className="material-icons" style={{ fontSize: "14px" }}>
warning
</span>
</Tooltip>
)}
</>
<TruncatedId id={transactionId} link={transactionLink} />
{isTransactionFromStardustGenesis && (
<Tooltip tooltipContent="This link opens the transaction on Chrysalis Mainnet" childrenClass="row middle">
<span className="material-icons" style={{ fontSize: "14px" }}>
warning
</span>
</Tooltip>
)}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import moment from "moment/moment";

import { DateHelper } from "~helpers/dateHelper";
import { OutputWithDetails } from "~helpers/hooks/useAddressHistory";
import { STARDUST_SUPPLY_INCREASE_TRANSACTION_ID, TransactionsHelper } from "~helpers/stardust/transactionsHelper";
import { STARDUST_SUPPLY_INCREASE_OUTPUT_TICKER, TransactionsHelper } from "~helpers/stardust/transactionsHelper";
import { formatAmount } from "~helpers/stardust/valueFormatHelper";
import { CHRYSALIS_MAINNET } from "~models/config/networkType";

Expand Down Expand Up @@ -64,12 +64,17 @@ export const getTransactionHistoryRecords = (

const isGenesisByDate = outputs.map((t) => t.milestoneTimestamp).some((milestoneTimestamp) => milestoneTimestamp === 0);

const milestoneIndexes = outputs.map((t) => t.milestoneIndex);
const isTransactionFromStardustGenesis = milestoneIndexes.some((milestoneIndex) =>
TransactionsHelper.isTransactionFromIotaStardustGenesis(network, milestoneIndex),
);
let stardustGenesisOutputId;
const isTransactionFromStardustGenesis = outputs.some(({ milestoneIndex, outputId }) => {
const isGenesis = TransactionsHelper.isTransactionFromIotaStardustGenesis(network, milestoneIndex);
if (isGenesis) {
stardustGenesisOutputId = outputId;
}

return isGenesis;
});

const transactionLink = getTransactionLink(network, transactionId, isTransactionFromStardustGenesis);
const transactionLink = getTransactionLink(network, transactionId, isTransactionFromStardustGenesis, stardustGenesisOutputId);

const isSpent = balanceChange <= 0;

Expand Down Expand Up @@ -106,8 +111,19 @@ export const calculateBalanceChange = (outputs: OutputWithDetails[]) => {
}, 0);
};

export const getTransactionLink = (network: string, transactionId: string, isTransactionFromStardustGenesis: boolean) => {
return isTransactionFromStardustGenesis && !transactionId.includes(STARDUST_SUPPLY_INCREASE_TRANSACTION_ID)
? `/${CHRYSALIS_MAINNET}/search/${transactionId}`
: `/${network}/transaction/${transactionId}`;
export const getTransactionLink = (
network: string,
transactionId: string,
isTransactionFromStardustGenesis: boolean,
outputId?: string,
) => {
if (isTransactionFromStardustGenesis && transactionId.includes(STARDUST_SUPPLY_INCREASE_OUTPUT_TICKER)) {
return `/${network}/output/${outputId}`;
}

if (isTransactionFromStardustGenesis && !transactionId.includes(STARDUST_SUPPLY_INCREASE_OUTPUT_TICKER)) {
return `/${CHRYSALIS_MAINNET}/search/${transactionId}`;
}

return `/${network}/transaction/${transactionId}`;
};
2 changes: 1 addition & 1 deletion client/src/app/routes/stardust/OutputPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const OutputPage: React.FC<RouteComponentProps<OutputPageProps>> = ({
} = outputMetadata ?? {};

const isTransactionFromStardustGenesis = TransactionsHelper.isTransactionFromIotaStardustGenesis(network, milestoneIndexBooked ?? 0);
const transactionLink = getTransactionLink(network, transactionId ?? "", isTransactionFromStardustGenesis);
const transactionLink = getTransactionLink(network, transactionId ?? "", isTransactionFromStardustGenesis, outputId);

return (
(output && (
Expand Down
2 changes: 1 addition & 1 deletion client/src/helpers/stardust/transactionsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const HEX_PARTICIPATE = "0x5041525449434950415445";
*/
export const STARDUST_GENESIS_MILESTONE = 7669900;

export const STARDUST_SUPPLY_INCREASE_TRANSACTION_ID = "0xb191c4bc825ac6983789e50545d5ef07a1d293a98ad974fc9498cb18";
export const STARDUST_SUPPLY_INCREASE_OUTPUT_TICKER = "0xb191c4bc825ac6983789e50545d5ef07a1d293a98ad974fc9498cb18";

export class TransactionsHelper {
public static async getInputsAndOutputs(
Expand Down
Loading