diff --git a/api/src/services/nova/novaApiService.ts b/api/src/services/nova/novaApiService.ts index 9ae7ccb6e..5fcd200ba 100644 --- a/api/src/services/nova/novaApiService.ts +++ b/api/src/services/nova/novaApiService.ts @@ -14,18 +14,12 @@ import { HexHelper } from "../../utils/hexHelper"; * Class to interact with the nova API. */ export class NovaApiService { - /** - * The network in context. - */ - private readonly network: INetwork; - /** * The client to use for requests. */ private readonly client: Client; constructor(network: INetwork) { - this.network = network; this.client = ServiceFactory.get(`client-${network.network}`); } diff --git a/client/src/app/AppUtils.tsx b/client/src/app/AppUtils.tsx index 0f240c6a2..6bb7207db 100644 --- a/client/src/app/AppUtils.tsx +++ b/client/src/app/AppUtils.tsx @@ -40,6 +40,8 @@ export const populateNetworkInfoNova = (networkName: string) => { name: networkName, tokenInfo: nodeInfo?.baseToken ?? {}, protocolVersion: protocolInfo?.parameters.version ?? -1, + protocolInfo, + latestConfirmedSlot: nodeInfo?.status?.latestConfirmedBlockSlot ?? -1, bech32Hrp: protocolInfo?.parameters.bech32Hrp ?? "", }); } diff --git a/client/src/app/components/nova/OutputView.tsx b/client/src/app/components/nova/OutputView.tsx index d138f6deb..b63544e7b 100644 --- a/client/src/app/components/nova/OutputView.tsx +++ b/client/src/app/components/nova/OutputView.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import DropdownIcon from "~assets/dropdown-arrow.svg?react"; import classNames from "classnames"; import { @@ -34,8 +34,8 @@ interface OutputViewProps { } const OutputView: React.FC = ({ outputId, output, showCopyAmount, isPreExpanded, isLinksDisabled }) => { - const [isExpanded, setIsExpanded] = React.useState(isPreExpanded ?? false); - const [isFormattedBalance, setIsFormattedBalance] = React.useState(true); + const [isExpanded, setIsExpanded] = useState(isPreExpanded ?? false); + const [isFormattedBalance, setIsFormattedBalance] = useState(true); const { bech32Hrp, name: network } = useNetworkInfoNova((s) => s.networkInfo); const aliasOrNftBech32 = buildAddressForAliasOrNft(outputId, output, bech32Hrp); diff --git a/client/src/app/routes/nova/OutputPage.tsx b/client/src/app/routes/nova/OutputPage.tsx index 918ff3c8a..b60844474 100644 --- a/client/src/app/routes/nova/OutputPage.tsx +++ b/client/src/app/routes/nova/OutputPage.tsx @@ -7,6 +7,9 @@ import OutputView from "~/app/components/nova/OutputView"; import { useOutputDetails } from "~/helpers/nova/hooks/useOutputDetails"; import CopyButton from "~/app/components/CopyButton"; import TruncatedId from "~/app/components/stardust/TruncatedId"; +import { useNetworkInfoNova } from "~/helpers/nova/networkInfo"; +import { buildManaDetailsForOutput, OutputManaDetails } from "~/helpers/nova/manaUtils"; +import { Converter } from "~/helpers/stardust/convertUtils"; import "./OutputPage.scss"; interface OutputPageProps { @@ -27,6 +30,7 @@ const OutputPage: React.FC> = ({ }, }) => { const { output, outputMetadataResponse, error } = useOutputDetails(network, outputId); + const { protocolInfo, latestConfirmedSlot } = useNetworkInfoNova((s) => s.networkInfo); if (error) { return ( @@ -45,7 +49,22 @@ const OutputPage: React.FC> = ({ ); } - const { blockId, transactionId, outputIndex, isSpent, transactionIdSpent } = outputMetadataResponse ?? {}; + const { blockId, included, spent } = outputMetadataResponse ?? {}; + + const transactionId = included?.transactionId ?? null; + const createdSlotIndex = (included?.slot as number) ?? null; + const spentSlotIndex = (spent?.slot as number) ?? null; + const isSpent = spentSlotIndex !== null; + const transactionIdSpent = spent?.transactionId ?? null; + const outputIndex = computeOutputIndexFromOutputId(outputId); + + let outputManaDetails: OutputManaDetails | null = null; + if (output && createdSlotIndex && protocolInfo) { + const untilSlotIndex = spentSlotIndex ? spentSlotIndex : latestConfirmedSlot > 0 ? latestConfirmedSlot : null; + outputManaDetails = untilSlotIndex + ? buildManaDetailsForOutput(output, createdSlotIndex, untilSlotIndex, protocolInfo.parameters) + : null; + } return ( (output && ( @@ -60,13 +79,7 @@ const OutputPage: React.FC> = ({
- +
@@ -125,6 +138,35 @@ const OutputPage: React.FC> = ({
)} + + {outputManaDetails && ( + <> +
+
Stored mana
+
+ {outputManaDetails.storedMana} +
+
+
+
Stored mana (decayed)
+
+ {outputManaDetails.storedManaDecayed} +
+
+
+
Potential mana
+
+ {outputManaDetails.potentialMana} +
+
+
+
Total mana
+
+ {outputManaDetails.totalMana} +
+
+ + )} @@ -134,4 +176,15 @@ const OutputPage: React.FC> = ({ ); }; +function computeOutputIndexFromOutputId(outputId: string | null) { + if (!outputId) { + return null; + } + + const outputIndexPart = outputId.slice(-4); + const outputIndexBigEndian = Converter.convertToBigEndian(outputIndexPart); + + return Number(outputIndexBigEndian); +} + export default OutputPage; diff --git a/client/src/helpers/nova/manaUtils.ts b/client/src/helpers/nova/manaUtils.ts new file mode 100644 index 000000000..62fb9dcb5 --- /dev/null +++ b/client/src/helpers/nova/manaUtils.ts @@ -0,0 +1,27 @@ +import { BasicOutput, Output, ProtocolParameters, Utils } from "@iota/sdk-wasm-nova/web"; + +export interface OutputManaDetails { + storedMana: string; + storedManaDecayed: string; + potentialMana: string; + totalMana: string; +} + +export function buildManaDetailsForOutput( + output: Output, + createdSlotIndex: number, + spentOrLatestSlotIndex: number, + protocolParameters: ProtocolParameters, +): OutputManaDetails { + const decayedMana = Utils.outputManaWithDecay(output, createdSlotIndex, spentOrLatestSlotIndex, protocolParameters); + const storedManaDecayed = BigInt(decayedMana.stored).toString(); + const potentialMana = BigInt(decayedMana.potential).toString(); + const totalMana = BigInt(decayedMana.stored) + BigInt(decayedMana.potential); + + return { + storedMana: (output as BasicOutput).mana?.toString(), + storedManaDecayed, + potentialMana, + totalMana: totalMana.toString(), + }; +} diff --git a/client/src/helpers/nova/networkInfo.ts b/client/src/helpers/nova/networkInfo.ts index 5685ee187..f0cc5944f 100644 --- a/client/src/helpers/nova/networkInfo.ts +++ b/client/src/helpers/nova/networkInfo.ts @@ -1,10 +1,12 @@ -import { INodeInfoBaseToken } from "@iota/sdk-wasm-nova/web"; +import { INodeInfoBaseToken, ProtocolInfo } from "@iota/sdk-wasm-nova/web"; import { create } from "zustand"; interface INetworkInfo { name: string; tokenInfo: INodeInfoBaseToken; protocolVersion: number; + protocolInfo: ProtocolInfo | null; + latestConfirmedSlot: number; bech32Hrp: string; } @@ -25,6 +27,8 @@ export const useNetworkInfoNova = create((set) => ({ useMetricPrefix: true, }, protocolVersion: -1, + protocolInfo: null, + latestConfirmedSlot: -1, bech32Hrp: "", }, setNetworkInfo: (networkInfo) => { diff --git a/setup_nova.sh b/setup_nova.sh index db350dfb2..ec1a0f118 100755 --- a/setup_nova.sh +++ b/setup_nova.sh @@ -1,6 +1,6 @@ #!/bin/bash SDK_DIR="iota-sdk" -TARGET_COMMIT="e8713d3b7d6b1f54055916b1a8f226b6cc2ce4c7" +TARGET_COMMIT="08318f6e3f92af609b89c571462ea31b32e7c121" if [ ! -d "$SDK_DIR" ]; then git clone -b 2.0 git@github.com:iotaledger/iota-sdk.git