diff --git a/client/package.json b/client/package.json index 72902e6fb..4f2d27a7f 100644 --- a/client/package.json +++ b/client/package.json @@ -115,6 +115,6 @@ ], "lint-staged": { "src/**/*.{ts,js,tsx,jsx}": "eslint --cache --fix", - "src/**/*.{ts,js,scss,css}": "prettier --ignore-path=.prettierignore --write" + "src/**/*.{ts,js,tsx,jsx,scss,css}": "prettier --ignore-path=.prettierignore --write" } } diff --git a/client/src/app/components/stardust/Input.tsx b/client/src/app/components/stardust/Input.tsx index 8fa0dcfb2..2c5be3314 100644 --- a/client/src/app/components/stardust/Input.tsx +++ b/client/src/app/components/stardust/Input.tsx @@ -2,14 +2,15 @@ /* eslint-disable jsdoc/require-returns */ import { Utils } from "@iota/sdk-wasm/web"; import classNames from "classnames"; -import React, { useContext, useState } from "react"; -import { useHistory, Link } from "react-router-dom"; -import Bech32Address from "./address/Bech32Address"; -import Output from "./Output"; +import React, { useContext, useEffect, useState } from "react"; +import { Link, useHistory } from "react-router-dom"; import DropdownIcon from "~assets/dropdown-arrow.svg?react"; import { formatAmount } from "~helpers/stardust/valueFormatHelper"; import { IInput } from "~models/api/stardust/IInput"; +import { IPreExpandedConfig } from "~models/components"; import NetworkContext from "../../context/NetworkContext"; +import Output from "./Output"; +import Bech32Address from "./address/Bech32Address"; interface InputProps { /** @@ -20,17 +21,25 @@ interface InputProps { * The network in context. */ readonly network: string; + /** + * Should the input be pre-expanded. + */ + readonly preExpandedConfig?: IPreExpandedConfig; } /** * Component which will display an Input on stardust. */ -const Input: React.FC = ({ input, network }) => { +const Input: React.FC = ({ input, network, preExpandedConfig }) => { const history = useHistory(); const { tokenInfo } = useContext(NetworkContext); - const [isExpanded, setIsExpanded] = useState(false); + const [isExpanded, setIsExpanded] = useState(preExpandedConfig?.isAllPreExpanded ?? preExpandedConfig?.isPreExpanded ?? false); const [isFormattedBalance, setIsFormattedBalance] = useState(true); + useEffect(() => { + setIsExpanded(preExpandedConfig?.isAllPreExpanded ?? preExpandedConfig?.isPreExpanded ?? isExpanded ?? false); + }, [preExpandedConfig]); + const fallbackInputView = (
setIsExpanded(!isExpanded)}> @@ -89,6 +98,7 @@ const Input: React.FC = ({ input, network }) => { amount={Number(input.output.output.amount)} network={network} showCopyAmount={true} + preExpandedConfig={preExpandedConfig} /> ) : ( fallbackInputView diff --git a/client/src/app/components/stardust/Output.tsx b/client/src/app/components/stardust/Output.tsx index 65af6ff5b..ceaba4cb6 100644 --- a/client/src/app/components/stardust/Output.tsx +++ b/client/src/app/components/stardust/Output.tsx @@ -61,17 +61,25 @@ class Output extends Component { super(props); this.state = { - isExpanded: this.props.isPreExpanded ?? false, + isExpanded: this.props.preExpandedConfig?.isAllPreExpanded ?? this.props.preExpandedConfig?.isPreExpanded ?? false, isFormattedBalance: true, }; } + componentDidUpdate(prevProps: Readonly): void { + if (prevProps.preExpandedConfig !== this.props.preExpandedConfig) { + this.setState({ + isExpanded: this.props.preExpandedConfig?.isAllPreExpanded ?? this.props.preExpandedConfig?.isPreExpanded ?? false, + }); + } + } + /** * Render the component. * @returns The node to render. */ public render(): ReactNode { - const { outputId, output, amount, showCopyAmount, network, isPreExpanded, displayFullOutputId, isLinksDisabled } = this.props; + const { outputId, output, amount, showCopyAmount, network, preExpandedConfig, displayFullOutputId, isLinksDisabled } = this.props; const { isExpanded, isFormattedBalance } = this.state; const tokenInfo: INodeInfoBaseToken = this.context.tokenInfo; @@ -223,33 +231,75 @@ class Output extends Component { {/* all output types except Treasury have common output conditions */} {output.type !== OutputType.Treasury && ( - {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => ( - - ))} - {(output as CommonOutput).features?.map((feature, idx) => ( - - ))} + {(output as CommonOutput).unlockConditions.map((unlockCondition, idx) => { + const isPreExpanded = + preExpandedConfig?.isAllPreExpanded ?? preExpandedConfig?.unlockConditions?.[idx] ?? false; + return ; + })} + {(output as CommonOutput).features?.map((feature, idx) => { + const isPreExpanded = + preExpandedConfig?.isAllPreExpanded ?? preExpandedConfig?.features?.[idx] ?? false; + return ( + + ); + })} {output.type === OutputType.Alias && - (output as AliasOutput).immutableFeatures?.map((immutableFeature, idx) => ( - - ))} + (output as AliasOutput).immutableFeatures?.map((immutableFeature, idx) => { + const isPreExpanded = + preExpandedConfig?.isAllPreExpanded ?? preExpandedConfig?.immutableFeatures?.[idx] ?? false; + return ( + + ); + })} {output.type === OutputType.Nft && - (output as NftOutput).immutableFeatures?.map((immutableFeature, idx) => ( - - ))} + (output as NftOutput).immutableFeatures?.map((immutableFeature, idx) => { + const isPreExpanded = + preExpandedConfig?.isAllPreExpanded ?? preExpandedConfig?.immutableFeatures?.[idx] ?? false; + return ( + + ); + })} {output.type === OutputType.Foundry && - (output as FoundryOutput).immutableFeatures?.map((immutableFeature, idx) => ( - - ))} - {(output as CommonOutput).nativeTokens?.map((token, idx) => ( - - ))} + (output as FoundryOutput).immutableFeatures?.map((immutableFeature, idx) => { + const isPreExpanded = + preExpandedConfig?.isAllPreExpanded ?? preExpandedConfig?.immutableFeatures?.[idx] ?? false; + return ( + + ); + })} + {(output as CommonOutput).nativeTokens?.map((token, idx) => { + const isPreExpanded = + preExpandedConfig?.isAllPreExpanded ?? preExpandedConfig?.nativeTokens?.[idx] ?? false; + return ( + + ); + })} )}
diff --git a/client/src/app/components/stardust/OutputProps.tsx b/client/src/app/components/stardust/OutputProps.tsx index 6c76dbe03..edd9d1382 100644 --- a/client/src/app/components/stardust/OutputProps.tsx +++ b/client/src/app/components/stardust/OutputProps.tsx @@ -1,4 +1,5 @@ import { Output } from "@iota/sdk-wasm/web"; +import { IPreExpandedConfig } from "~models/components"; export interface OutputProps { /** @@ -21,11 +22,6 @@ export interface OutputProps { */ showCopyAmount: boolean; - /** - * Should the output be pre-expanded. - */ - isPreExpanded?: boolean; - /** * Should the outputId be displayed in full (default truncated). */ @@ -40,4 +36,9 @@ export interface OutputProps { * Disable links if block is conflicting. */ isLinksDisabled?: boolean; + + /** + * Should the output and its fields be pre-expanded. + */ + preExpandedConfig?: IPreExpandedConfig; } diff --git a/client/src/app/components/stardust/address/AddressBalance.tsx b/client/src/app/components/stardust/address/AddressBalance.tsx index c548a674f..432690d4f 100644 --- a/client/src/app/components/stardust/address/AddressBalance.tsx +++ b/client/src/app/components/stardust/address/AddressBalance.tsx @@ -21,13 +21,13 @@ interface AddressBalanceProps { /** * The storage rent balance. */ - readonly storageRentBalance: number | null; + readonly storageDeposit: number | null; } const CONDITIONAL_BALANCE_INFO = "These funds reside within outputs with additional unlock conditions which might be potentially un-lockable"; -const AddressBalance: React.FC = ({ balance, spendableBalance, storageRentBalance }) => { +const AddressBalance: React.FC = ({ balance, spendableBalance, storageDeposit }) => { const { name: network, tokenInfo } = useContext(NetworkContext); const [formatBalanceFull, setFormatBalanceFull] = useState(false); const [formatConditionalBalanceFull, setFormatConditionalBalanceFull] = useState(false); @@ -101,14 +101,7 @@ const AddressBalance: React.FC = ({ balance, spendableBalan false, conditionalBalance, )} - {buildBalanceView( - "Storage Deposit", - formatStorageBalanceFull, - setFormatStorageBalanceFull, - false, - false, - storageRentBalance, - )} + {buildBalanceView("Storage Deposit", formatStorageBalanceFull, setFormatStorageBalanceFull, false, false, storageDeposit)} ); diff --git a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx index a7a023fe4..3a1d5febf 100644 --- a/client/src/app/components/stardust/block/payload/TransactionPayload.tsx +++ b/client/src/app/components/stardust/block/payload/TransactionPayload.tsx @@ -1,104 +1,80 @@ -import React, { ReactNode } from "react"; -import { TransactionPayloadProps } from "./TransactionPayloadProps"; -import { TransactionPayloadState } from "./TransactionPayloadState"; +import React, { useContext, useEffect, useState } from "react"; +import NetworkContext from "~/app/context/NetworkContext"; import transactionPayloadMessage from "~assets/modals/stardust/block/transaction-payload.json"; -import NetworkContext from "../../../../context/NetworkContext"; -import AsyncComponent from "../../../AsyncComponent"; +import { getInputsPreExpandedConfig, getOutputsPreExpandedConfig } from "~helpers/stardust/preExpandedConfig"; +import { IPreExpandedConfig } from "~models/components"; import Modal from "../../../Modal"; import Input from "../../Input"; import Output from "../../Output"; import Unlocks from "../../Unlocks"; import "./TransactionPayload.scss"; +import { TransactionPayloadProps } from "./TransactionPayloadProps"; /** * Component which will display a transaction payload. */ -class TransactionPayload extends AsyncComponent { - /** - * The component context type. - */ - public static contextType = NetworkContext; - - /** - * The component context. - */ - public declare context: React.ContextType; - - /** - * Create a new instance of TransactionPayload. - * @param props The props. - */ - constructor(props: TransactionPayloadProps) { - super(props); - - this.state = { - isFormattedBalance: true, - }; - } +const TransactionPayload: React.FC = ({ network, inputs, unlocks, outputs, header, isLinksDisabled }) => { + const [inputsPreExpandedConfig, setInputsPreExpandedConfig] = useState([]); + const { bech32Hrp } = useContext(NetworkContext); - /** - * The component mounted. - */ - public async componentDidMount(): Promise { - super.componentDidMount(); - } + const outputsPreExpandedConfig = getOutputsPreExpandedConfig(outputs); - /** - * Render the component. - * @returns The node to render. - */ - public render(): ReactNode { - const { network, inputs, unlocks, outputs, header, isLinksDisabled } = this.props; + useEffect(() => { + if (bech32Hrp) { + const inputsPreExpandedConfig = getInputsPreExpandedConfig(inputs, unlocks, bech32Hrp); + setInputsPreExpandedConfig(inputsPreExpandedConfig); + } + }, [bech32Hrp]); - return ( -
- {header && ( -
-
-

{header}

- -
+ return ( +
+ {header && ( +
+
+

{header}

+
- )} -
-
-
-

From

- - {inputs.length} -
-
- {inputs.map((input, idx) => ( - - ))} - -
+
+ )} +
+
+
+

From

+ + {inputs.length} +
+
+ {inputs.map((input, idx) => ( + + ))} +
+
-
-
-

To

- - {outputs.length} -
-
- {outputs.map((output, idx) => ( - - ))} -
+
+
+

To

+ + {outputs.length} +
+
+ {outputs.map((output, idx) => ( + + ))}
- ); - } -} +
+ ); +}; export default TransactionPayload; diff --git a/client/src/app/routes/stardust/AddressPage.tsx b/client/src/app/routes/stardust/AddressPage.tsx index 8972379d8..f5a8deeb5 100644 --- a/client/src/app/routes/stardust/AddressPage.tsx +++ b/client/src/app/routes/stardust/AddressPage.tsx @@ -24,7 +24,7 @@ const AddressPage: React.FC> = ({ bech32AddressDetails, balance, availableBalance, - storageRentBalance, + storageDeposit, isBasicOutputsLoading, isAliasOutputsLoading, isNftOutputsLoading, @@ -88,7 +88,7 @@ const AddressPage: React.FC> = ({ )}
diff --git a/client/src/app/routes/stardust/AddressState.ts b/client/src/app/routes/stardust/AddressState.ts index 5b1d46526..298962c40 100644 --- a/client/src/app/routes/stardust/AddressState.ts +++ b/client/src/app/routes/stardust/AddressState.ts @@ -1,17 +1,20 @@ import { Bech32Helper } from "@iota/iota.js"; import { - HexEncodedString, + AddressType, AliasOutput, + BasicOutput, + FeatureType, + HexEncodedString, MetadataFeature, + Output, OutputResponse, OutputType, - AddressType, - Output, - BasicOutput, - FeatureType, } from "@iota/sdk-wasm/web"; import { Reducer, useContext, useEffect, useReducer } from "react"; import { useLocation, useParams } from "react-router-dom"; +import { useAliasContainsDID } from "~/helpers/hooks/useAliasContainsDID"; +import { useResolvedDID } from "~/helpers/hooks/useResolvedDID"; +import { IDIDResolverResponse } from "~/models/api/IDIDResolverResponse"; import { useAddressAliasOutputs } from "~helpers/hooks/useAddressAliasOutputs"; import { useAddressBalance } from "~helpers/hooks/useAddressBalance"; import { useAddressBasicOutputs } from "~helpers/hooks/useAddressBasicOutputs"; @@ -30,15 +33,12 @@ import { IBech32AddressDetails } from "~models/api/IBech32AddressDetails"; import { IParticipation } from "~models/api/stardust/participation/IParticipation"; import NetworkContext from "../../context/NetworkContext"; import { AddressRouteProps } from "../AddressRouteProps"; -import { useAliasContainsDID } from "~/helpers/hooks/useAliasContainsDID"; -import { useResolvedDID } from "~/helpers/hooks/useResolvedDID"; -import { IDIDResolverResponse } from "~/models/api/IDIDResolverResponse"; export interface IAddressState { bech32AddressDetails: IBech32AddressDetails | null; balance: number | null; availableBalance: number | null; - storageRentBalance: number | null; + storageDeposit: number | null; addressOutputs: OutputResponse[] | null; addressBasicOutputs: OutputResponse[] | null; isBasicOutputsLoading: boolean; @@ -70,7 +70,7 @@ const initialState = { bech32AddressDetails: null, balance: null, availableBalance: null, - storageRentBalance: null, + storageDeposit: null, addressOutputs: null, addressBasicOutputs: null, isBasicOutputsLoading: true, @@ -118,17 +118,23 @@ export const useAddressPageState = (): [IAddressState, React.Dispatch { - if (addressBasicOutputs && addressAliasOutputs && addressNftOutputs) { - const mergedOutputResponses = [...addressBasicOutputs, ...addressAliasOutputs, ...addressNftOutputs]; - const outputs = mergedOutputResponses.map((or) => or.output); - const storageRentBalanceUpdate = TransactionsHelper.computeStorageRentBalance(outputs, rentStructure); - - setState({ - addressOutputs: mergedOutputResponses, - storageRentBalance: storageRentBalanceUpdate, - }); + const addressOutputs = + [...(addressBasicOutputs ?? []), ...(addressAliasOutputs ?? []), ...(addressNftOutputs ?? [])].filter((o) => o !== null) ?? []; + let outputsComputedInStorageDeposit = addressOutputs?.map((or) => or.output); + const addressOutputItself = nftOutput ?? aliasOutput; + if (addressOutputItself) { + outputsComputedInStorageDeposit = [...outputsComputedInStorageDeposit, addressOutputItself]; } + const storageDeposit = TransactionsHelper.computeStorageDeposit(outputsComputedInStorageDeposit, rentStructure); + + setState({ + addressOutputs, + storageDeposit, + }); if (addressBasicOutputs && !state.participations) { let foundParticipations: IParticipation[] = []; for (const outputResponse of addressBasicOutputs) { diff --git a/client/src/app/routes/stardust/OutputList.tsx b/client/src/app/routes/stardust/OutputList.tsx index cd6d732a4..fed1fe2d9 100644 --- a/client/src/app/routes/stardust/OutputList.tsx +++ b/client/src/app/routes/stardust/OutputList.tsx @@ -72,7 +72,7 @@ const OutputList: React.FC> = ({ output={item.outputDetails.output} amount={Number(item.outputDetails.output.amount)} showCopyAmount={true} - isPreExpanded={false} + preExpandedConfig={{ isPreExpanded: false }} />
))} @@ -108,7 +108,7 @@ const OutputList: React.FC> = ({ output={item.outputDetails.output} amount={Number(item.outputDetails.output.amount)} showCopyAmount={true} - isPreExpanded={false} + preExpandedConfig={{ isPreExpanded: false }} />
))} diff --git a/client/src/app/routes/stardust/OutputPage.tsx b/client/src/app/routes/stardust/OutputPage.tsx index 03461cda6..4ec091e8d 100644 --- a/client/src/app/routes/stardust/OutputPage.tsx +++ b/client/src/app/routes/stardust/OutputPage.tsx @@ -72,7 +72,7 @@ const OutputPage: React.FC> = ({ output={output} amount={Number(output.amount)} showCopyAmount={true} - isPreExpanded={true} + preExpandedConfig={{ isAllPreExpanded: true }} />
diff --git a/client/src/helpers/hooks/useAddressBalance.ts b/client/src/helpers/hooks/useAddressBalance.ts index 7188cabc2..ad9bca7e4 100644 --- a/client/src/helpers/hooks/useAddressBalance.ts +++ b/client/src/helpers/hooks/useAddressBalance.ts @@ -1,22 +1,21 @@ +import { AliasOutput, NftOutput } from "@iota/sdk-wasm/web"; import { useEffect, useState } from "react"; -import { AliasOutput, OutputType } from "@iota/sdk-wasm/web"; -import { useIsMounted } from "./useIsMounted"; import { ServiceFactory } from "~factories/serviceFactory"; import { STARDUST } from "~models/config/protocolVersion"; import { StardustApiClient } from "~services/stardust/stardustApiClient"; -import { fetchAliasDetailsOutput } from "~helpers/hooks/useAliasDetails"; +import { useIsMounted } from "./useIsMounted"; /** * Fetch the address balance * @param network The Network in context * @param address The bech32 address - * @param options + * @param output The output wrapping the address, used to add the output amount to the balance * @returns The address balance, signature locked balance and a loading bool. */ export function useAddressBalance( network: string, address: string | null, - options?: { aliasId?: string | null }, + output: AliasOutput | NftOutput | null, ): [number | null, number | null, boolean] { const isMounted = useIsMounted(); const [apiClient] = useState(ServiceFactory.get(`api-client-${STARDUST}`)); @@ -30,18 +29,26 @@ export function useAddressBalance( // eslint-disable-next-line no-void void (async () => { const response = await apiClient.addressBalanceChronicle({ network, address }); - const output = options?.aliasId ? await fetchAliasDetailsOutput(network, options.aliasId) : null; if (response?.totalBalance !== undefined && isMounted) { - setBalance(response.totalBalance); - const availableBalance = consolidateOutputBalance(response.totalBalance, output); - setAvailableBalance(availableBalance ?? null); + let totalBalance = response.totalBalance; + let availableBalance = response.availableBalance ?? 0; + if (output) { + totalBalance = totalBalance + Number(output.amount); + availableBalance = availableBalance + Number(output.amount); + } + setBalance(totalBalance); + setAvailableBalance(availableBalance > 0 ? availableBalance : null); } else if (isMounted) { // Fallback balance from iotajs (node) const addressDetailsWithBalance = await apiClient.addressBalance({ network, address }); if (addressDetailsWithBalance && isMounted) { - setBalance(Number(addressDetailsWithBalance.balance)); + let totalBalance = Number(addressDetailsWithBalance.balance); + if (output) { + totalBalance = totalBalance + Number(output.amount); + } + setBalance(totalBalance); setAvailableBalance(null); } } @@ -49,19 +56,7 @@ export function useAddressBalance( } else { setIsLoading(false); } - }, [network, address, options?.aliasId]); + }, [network, address, output]); return [balance, availableBalance, isLoading]; } - -const consolidateOutputBalance = (currentBalance: number | string | null, additionalOutput?: AliasOutput | null) => { - if (!currentBalance) return null; - - let total = currentBalance ? parseInt(currentBalance as string) : 0; - - if (additionalOutput?.type === OutputType.Alias) { - total += parseInt(additionalOutput.amount); - } - - return total; -}; diff --git a/client/src/helpers/stardust/preExpandedConfig.ts b/client/src/helpers/stardust/preExpandedConfig.ts new file mode 100644 index 000000000..a1badc321 --- /dev/null +++ b/client/src/helpers/stardust/preExpandedConfig.ts @@ -0,0 +1,127 @@ +import { + AddressUnlockCondition, + CommonOutput, + ExpirationUnlockCondition, + GovernorAddressUnlockCondition, + ReferenceUnlock, + SignatureUnlock, + StateControllerAddressUnlockCondition, + Unlock, + UnlockConditionType, + UnlockType, + Utils, +} from "@iota/sdk-wasm/web"; +import { Bech32AddressHelper } from "~/helpers/stardust/bech32AddressHelper"; +import { IInput } from "~models/api/stardust/IInput"; +import { IOutput } from "~models/api/stardust/IOutput"; +import { IPreExpandedConfig } from "~models/components"; + +const OUTPUT_EXPAND_CONDITIONS: UnlockConditionType[] = [ + UnlockConditionType.Address, + UnlockConditionType.StateControllerAddress, + UnlockConditionType.GovernorAddress, +]; +const INPUT_EXPAND_CONDITIONS: UnlockConditionType[] = [...OUTPUT_EXPAND_CONDITIONS, UnlockConditionType.Expiration]; + +/** + * Get the preExpandedConfig for the inputs. + * Expand the input and its unlock conditions if, given the unlocks, we match unlock conditions. + * @param inputs The inputs to calculate the preExpandedConfig for. + * @param unlocks The unlocks used to spend the inputs. + * @param bech32Hrp The bech32 hrp. + * @returns The preExpandedConfig for the inputs. + */ +export function getInputsPreExpandedConfig(inputs: IInput[], unlocks: Unlock[], bech32Hrp: string): IPreExpandedConfig[] { + const inputsPreExpandedConfig: IPreExpandedConfig[] = inputs.map((input, idx) => { + const commonOutput = input?.output?.output as unknown as CommonOutput; + let preExpandedConfig: IPreExpandedConfig = {}; + if (commonOutput) { + const matchExpandCondition = commonOutput.unlockConditions?.find((unlockCondition) => + INPUT_EXPAND_CONDITIONS.includes(unlockCondition.type), + ); + preExpandedConfig = { + isPreExpanded: !!matchExpandCondition, + }; + if (input?.output?.output && "unlockConditions" in input.output.output) { + const commmonOutput = input.output.output as unknown as CommonOutput; + let unlock = unlocks[idx]; + if (unlock.type === UnlockType.Reference) { + const referenceUnlock = unlock as ReferenceUnlock; + unlock = unlocks[referenceUnlock.reference]; + } + const unlockSignatureAddress = Utils.hexPublicKeyToBech32Address( + (unlock as SignatureUnlock).signature.publicKey, + bech32Hrp, + ); + preExpandedConfig = { + ...preExpandedConfig, + unlockConditions: commmonOutput.unlockConditions?.map((unlockCondition) => { + switch (unlockCondition.type) { + case UnlockConditionType.Address: { + const unlockAddress = Bech32AddressHelper.buildAddress( + bech32Hrp, + (unlockCondition as AddressUnlockCondition).address, + )?.bech32; + return unlockAddress === unlockSignatureAddress; + } + case UnlockConditionType.Expiration: { + const unlockAddress = Bech32AddressHelper.buildAddress( + bech32Hrp, + (unlockCondition as ExpirationUnlockCondition).returnAddress, + )?.bech32; + return unlockAddress === unlockSignatureAddress; + } + case UnlockConditionType.StateControllerAddress: { + const unlockAddress = Bech32AddressHelper.buildAddress( + bech32Hrp, + (unlockCondition as StateControllerAddressUnlockCondition).address, + )?.bech32; + return unlockAddress === unlockSignatureAddress; + } + case UnlockConditionType.GovernorAddress: { + const unlockAddress = Bech32AddressHelper.buildAddress( + bech32Hrp, + (unlockCondition as GovernorAddressUnlockCondition).address, + )?.bech32; + return unlockAddress === unlockSignatureAddress; + } + default: + return false; + } + }), + }; + } + } + return preExpandedConfig; + }); + return inputsPreExpandedConfig; +} + +/** + * Get the preExpandedConfig for the outputs. + * Expand the output and its relevant receiver address related unlock conditions. + * @param outputs The outputs to calculate the preExpandedConfig for. + * @returns The preExpandedConfig for the outputs. + */ +export function getOutputsPreExpandedConfig(outputs: IOutput[]): IPreExpandedConfig[] { + const outputsPreExpandedConfig: IPreExpandedConfig[] = outputs.map((output) => { + const commonOutput = output.output as CommonOutput; + let preExpandedConfig: IPreExpandedConfig = {}; + if (commonOutput) { + const matchExpandCondition = commonOutput.unlockConditions?.find((unlockCondition) => + OUTPUT_EXPAND_CONDITIONS.includes(unlockCondition.type), + ); + preExpandedConfig = { + isPreExpanded: !!matchExpandCondition, + }; + preExpandedConfig = { + ...preExpandedConfig, + unlockConditions: commonOutput.unlockConditions?.map((unlockCondition) => + OUTPUT_EXPAND_CONDITIONS.includes(unlockCondition.type), + ), + }; + } + return preExpandedConfig; + }); + return outputsPreExpandedConfig; +} diff --git a/client/src/helpers/stardust/transactionsHelper.ts b/client/src/helpers/stardust/transactionsHelper.ts index 3fd705069..1a04615db 100644 --- a/client/src/helpers/stardust/transactionsHelper.ts +++ b/client/src/helpers/stardust/transactionsHelper.ts @@ -237,7 +237,7 @@ export class TransactionsHelper { return HexHelper.toBigInt256(nftId).eq(bigInt.zero) ? Utils.computeNftId(outputId) : nftId; } - public static computeStorageRentBalance(outputs: Output[], rentStructure: IRent): number { + public static computeStorageDeposit(outputs: Output[], rentStructure: IRent): number { const outputsWithoutSdruc = outputs.filter((output) => { if (output.type === OutputType.Treasury) { return false; diff --git a/client/src/models/components/IPreExpandedConfig.tsx b/client/src/models/components/IPreExpandedConfig.tsx new file mode 100644 index 000000000..d12370b70 --- /dev/null +++ b/client/src/models/components/IPreExpandedConfig.tsx @@ -0,0 +1,10 @@ +export interface IPreExpandedConfig { + isPreExpanded?: boolean; + unlockConditions?: boolean[]; + features?: boolean[]; + immutableFeatures?: boolean[]; + nativeTokens?: boolean[]; + + // generic to expand all + isAllPreExpanded?: boolean; +} diff --git a/client/src/models/components/index.tsx b/client/src/models/components/index.tsx new file mode 100644 index 000000000..6d3e26606 --- /dev/null +++ b/client/src/models/components/index.tsx @@ -0,0 +1 @@ +export * from "./IPreExpandedConfig";