diff --git a/packages/app/src/systems/Core/components/CopyButton/CopyButton.tsx b/packages/app/src/systems/Core/components/CopyButton/CopyButton.tsx
new file mode 100644
index 000000000..038f625f9
--- /dev/null
+++ b/packages/app/src/systems/Core/components/CopyButton/CopyButton.tsx
@@ -0,0 +1,33 @@
+import type { ButtonProps } from '@fuels/ui';
+import { Button, Copyable } from '@fuels/ui';
+
+type CopyButtonProps = ButtonProps & {
+ value: string;
+ text?: string;
+};
+
+const COPY_ICON_SIZES = {
+ '1': 15,
+ '2': 19,
+ '3': 24,
+ '4': 29,
+};
+
+const CopyButton = ({ value, text = 'Copy', ...props }: CopyButtonProps) => {
+ const size = props.size || '1';
+ const variant = props.variant || 'soft';
+
+ return (
+
+ );
+};
+
+export default CopyButton;
diff --git a/packages/app/src/systems/Core/components/JsonViewer/JsonViewer.tsx b/packages/app/src/systems/Core/components/JsonViewer/JsonViewer.tsx
new file mode 100644
index 000000000..a802c1fe7
--- /dev/null
+++ b/packages/app/src/systems/Core/components/JsonViewer/JsonViewer.tsx
@@ -0,0 +1,34 @@
+import { useRadixTheme } from '@fuels/ui';
+import {
+ JsonView,
+ collapseAllNested,
+ darkStyles,
+ defaultStyles,
+} from 'react-json-view-lite';
+import { tv } from 'tailwind-variants';
+
+export type JsonViewerProps = {
+ data: object | unknown[];
+};
+
+export function JsonViewer({ data, ...props }: JsonViewerProps) {
+ const classes = styles();
+ const ctx = useRadixTheme();
+ return (
+
+ );
+}
+
+const styles = tv({
+ slots: {
+ json: 'bg-transparent text-sm py-2 px-1',
+ },
+});
diff --git a/packages/app/src/systems/Core/components/ViewMode/ViewMode.tsx b/packages/app/src/systems/Core/components/ViewMode/ViewMode.tsx
new file mode 100644
index 000000000..abfd400d7
--- /dev/null
+++ b/packages/app/src/systems/Core/components/ViewMode/ViewMode.tsx
@@ -0,0 +1,53 @@
+import { Flex, Text } from '@fuels/ui';
+import { tv } from 'tailwind-variants';
+
+export enum ViewModes {
+ Simple = 'Simple',
+ Advanced = 'Advanced',
+}
+
+export type ViewModeProps = {
+ mode: ViewModes;
+ onChange: (mode: ViewModes) => void;
+};
+
+export function ViewMode({ mode, onChange }: ViewModeProps) {
+ const classes = styles();
+
+ return (
+
+ onChange(ViewModes.Simple)}
+ >
+ Simple
+
+ onChange(ViewModes.Advanced)}
+ >
+ Advanced
+
+
+ );
+}
+
+const styles = tv({
+ slots: {
+ root: 'bg-gray-3 p-1 rounded h-9',
+ viewItem: [
+ 'flex-1 rounded cursor-pointer',
+ 'data-[mode=Simple]:px-6',
+ 'data-[mode=Advanced]:px-3',
+ 'data-[active=true]:bg-gray-1 data-[active=true]:cursor-default',
+ ],
+ },
+});
diff --git a/packages/app/src/systems/Transaction/component/TxScreen/TxScreenAdvanced.tsx b/packages/app/src/systems/Transaction/component/TxScreen/TxScreenAdvanced.tsx
new file mode 100644
index 000000000..ceb8fdcc6
--- /dev/null
+++ b/packages/app/src/systems/Transaction/component/TxScreen/TxScreenAdvanced.tsx
@@ -0,0 +1,66 @@
+'use client';
+
+import type { Maybe } from '@fuel-explorer/graphql';
+import { Button, Card, Flex, ScrollArea, Text, VStack } from '@fuels/ui';
+import { IconChevronDown } from '@tabler/icons-react';
+import { useState } from 'react';
+import { tv } from 'tailwind-variants';
+import CopyButton from '~/systems/Core/components/CopyButton/CopyButton';
+import { JsonViewer } from '~/systems/Core/components/JsonViewer/JsonViewer';
+
+import type { TransactionNode } from '../../types';
+
+type TxScreenProps = {
+ transaction?: Maybe;
+};
+
+export function TxScreenAdvanced({ transaction: tx }: TxScreenProps) {
+ const [compact, setCompact] = useState(true);
+ const classes = styles();
+ if (!tx) return null;
+
+ return (
+
+
+
+
+
+ JSON
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = tv({
+ slots: {
+ root: [
+ 'group transition-[max-height] max-h-[75vh]',
+ 'data-[compact=true]:max-h-[400px]',
+ ],
+ cardHeader: 'border-b border-card-border py-3 flex-none',
+ cardMiddle: 'flex-1 [&_.rt-ScrollAreaViewport_>div>div]:max-w-[1120px]',
+ cardFooter: [
+ 'border-t border-card-border',
+ 'py-3 self-stretch flex-none justify-center',
+ 'group-data-[compact=true]:[&_svg]:-rotate-180 [&_svg]:transition-transform',
+ ],
+ },
+});
diff --git a/packages/app/src/systems/Transaction/component/TxScreen/TxScreenSimple.tsx b/packages/app/src/systems/Transaction/component/TxScreen/TxScreenSimple.tsx
new file mode 100644
index 000000000..2ab9ec8ef
--- /dev/null
+++ b/packages/app/src/systems/Transaction/component/TxScreen/TxScreenSimple.tsx
@@ -0,0 +1,144 @@
+'use client';
+
+import type {
+ GroupedInput,
+ GroupedOutput,
+ Maybe,
+} from '@fuel-explorer/graphql';
+import {
+ Badge,
+ Box,
+ EntityItem,
+ Flex,
+ Grid,
+ Heading,
+ Icon,
+ Text,
+ VStack,
+} from '@fuels/ui';
+import { IconArrowDown } from '@tabler/icons-react';
+import { bn } from 'fuels';
+import { EmptyCard } from '~/systems/Core/components/EmptyCard/EmptyCard';
+
+import { TxInfo } from '../../component/TxInfo/TxInfo';
+import { TxInput } from '../../component/TxInput/TxInput';
+import { TxOutput } from '../../component/TxOutput/TxOutput';
+import type { TransactionNode, TxStatus } from '../../types';
+import { TX_INTENT_MAP, TxIcon } from '../TxIcon/TxIcon';
+import { TxScripts } from '../TxScripts/TxScripts';
+
+type TxScreenProps = {
+ transaction: TransactionNode;
+};
+
+export function TxScreenSimple({ transaction: tx }: TxScreenProps) {
+ const hasInputs = tx.groupedInputs?.length ?? 0 > 0;
+ const hasOutputs = tx.groupedOutputs?.length ?? 0 > 0;
+ const title = tx.title as string;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {tx.statusType}
+
+
+
+
+
+
+ {tx.time?.fromNow}
+
+ {tx.blockHeight && #{tx.blockHeight}}
+
+ {bn(tx.gasUsed).format()}
+
+
+
+
+
+
+
+ Inputs
+
+ {hasInputs ? (
+ tx.groupedInputs?.map((input) => (
+
+ ))
+ ) : (
+
+ No Inputs
+
+ This transaction does not have any inputs.
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ Outputs
+
+ {hasOutputs ? (
+ tx.groupedOutputs?.map((output) => (
+
+ ))
+ ) : (
+
+ No Outputs
+
+ This transaction does not have any outputs.
+
+
+ )}
+
+
+
+
+ );
+}
+
+function getInputId(input?: Maybe) {
+ if (!input) return 0;
+ if (input.type === 'InputCoin') return input.assetId;
+ if (input.type === 'InputContract') return input.contractId;
+ return input.sender;
+}
+
+function getOutputId(output?: Maybe) {
+ if (!output) return 0;
+ if (output.type === 'ContractOutput') return output.inputIndex;
+ if (output.type === 'ContractCreated') return output.contract?.id ?? 0;
+ if (output.type === 'MessageOutput') return output.recipient;
+ return output.assetId;
+}
diff --git a/packages/app/src/systems/Transaction/component/TxScripts/TxScripts.tsx b/packages/app/src/systems/Transaction/component/TxScripts/TxScripts.tsx
index b7be12d6c..1752fd2fb 100644
--- a/packages/app/src/systems/Transaction/component/TxScripts/TxScripts.tsx
+++ b/packages/app/src/systems/Transaction/component/TxScripts/TxScripts.tsx
@@ -13,7 +13,6 @@ import {
Icon,
Text,
VStack,
- useRadixTheme,
} from '@fuels/ui';
import {
IconFold,
@@ -23,15 +22,10 @@ import {
import { bn } from 'fuels';
import Image from 'next/image';
import { useState } from 'react';
-import {
- JsonView,
- defaultStyles,
- darkStyles,
- collapseAllNested,
-} from 'react-json-view-lite';
import { tv } from 'tailwind-variants';
import { useAsset } from '~/systems/Asset/hooks/useAsset';
import { EmptyCard } from '~/systems/Core/components/EmptyCard/EmptyCard';
+import { JsonViewer } from '~/systems/Core/components/JsonViewer/JsonViewer';
import type { TransactionNode } from '../../types';
@@ -52,7 +46,6 @@ export type TxScriptRowProps = BaseProps<{
function TxScriptRow({ item }: TxScriptRowProps) {
const asset = useAsset(item.assetId);
const classes = styles();
- const ctx = useRadixTheme();
const amount = bn(item.amount);
const isDanger =
item.receiptType === 'PANIC' ||
@@ -97,14 +90,7 @@ function TxScriptRow({ item }: TxScriptRowProps) {
-
+
);
@@ -198,7 +184,6 @@ const styles = tv({
slots: {
icon: 'transition-transform group-data-[state=closed]:hover:rotate-180 group-data-[state=open]:rotate-180',
utxos: 'bg-gray-3 mx-3 mb-3 p-0 rounded',
- json: 'bg-transparent text-sm py-2 px-1',
lines: [
'relative flex-1 border-t border-b border-border',
'before:h-[1px] before:absolute before:top-1/2 before:left-0 before:w-full before:bg-border',
diff --git a/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx b/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx
index b67f5da16..09e94a75f 100644
--- a/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx
+++ b/packages/app/src/systems/Transaction/screens/TxScreen/TxScreen.tsx
@@ -1,155 +1,39 @@
'use client';
-import type {
- GroupedInput,
- GroupedOutput,
- Maybe,
-} from '@fuel-explorer/graphql';
-import {
- Badge,
- Box,
- EntityItem,
- Flex,
- Grid,
- Heading,
- Icon,
- VStack,
- Text,
- Address,
-} from '@fuels/ui';
-import { IconArrowDown, IconChecklist } from '@tabler/icons-react';
-import { bn } from 'fuels';
-import { EmptyCard } from '~/systems/Core/components/EmptyCard/EmptyCard';
+import type { Maybe } from '@fuel-explorer/graphql';
+import { Address, Flex, VStack } from '@fuels/ui';
+import { IconChecklist } from '@tabler/icons-react';
+import { useState } from 'react';
import { PageTitle } from '~/systems/Core/components/PageTitle/PageTitle';
+import {
+ ViewMode,
+ ViewModes,
+} from '~/systems/Core/components/ViewMode/ViewMode';
-import { TX_INTENT_MAP, TxIcon } from '../../component/TxIcon/TxIcon';
-import { TxInfo } from '../../component/TxInfo/TxInfo';
-import { TxInput } from '../../component/TxInput/TxInput';
-import { TxOutput } from '../../component/TxOutput/TxOutput';
-import { TxScripts } from '../../component/TxScripts/TxScripts';
-import type { TransactionNode, TxStatus } from '../../types';
+import { TxScreenAdvanced } from '../../component/TxScreen/TxScreenAdvanced';
+import { TxScreenSimple } from '../../component/TxScreen/TxScreenSimple';
+import type { TransactionNode } from '../../types';
type TxScreenProps = {
transaction?: Maybe;
};
export function TxScreen({ transaction: tx }: TxScreenProps) {
+ const [viewMode, setViewMode] = useState(ViewModes.Simple);
+
if (!tx) return null;
- const hasInputs = tx.groupedInputs?.length ?? 0 > 0;
- const hasOutputs = tx.groupedOutputs?.length ?? 0 > 0;
- const title = tx.title as string;
return (
-
+
}>
Transaction
+
+
+
-
-
-
-
-
-
-
-
-
-
-
- {tx.statusType}
-
-
-
-
-
-
- {tx.time?.fromNow}
-
- {tx.blockHeight && (
- #{tx.blockHeight}
- )}
-
- {bn(tx.gasUsed).format()}
-
-
-
-
-
-
-
- Inputs
-
- {hasInputs ? (
- tx.groupedInputs?.map((input) => (
-
- ))
- ) : (
-
- No Inputs
-
- This transaction does not have any inputs.
-
-
- )}
-
-
-
-
-
-
-
-
-
-
- Outputs
-
- {hasOutputs ? (
- tx.groupedOutputs?.map((output) => (
-
- ))
- ) : (
-
- No Outputs
-
- This transaction does not have any outputs.
-
-
- )}
-
-
-
-
+ {viewMode === ViewModes.Simple && }
+ {viewMode === ViewModes.Advanced && }
);
}
-
-function getInputId(input?: Maybe) {
- if (!input) return 0;
- if (input.type === 'InputCoin') return input.assetId;
- if (input.type === 'InputContract') return input.contractId;
- return input.sender;
-}
-
-function getOutputId(output?: Maybe) {
- if (!output) return 0;
- if (output.type === 'ContractOutput') return output.inputIndex;
- if (output.type === 'ContractCreated') return output.contract?.id ?? 0;
- if (output.type === 'MessageOutput') return output.recipient;
- return output.assetId;
-}
diff --git a/packages/graphql/src/queries/tx-fragments.graphql b/packages/graphql/src/queries/tx-fragments.graphql
index 1b47c0cea..9dfa38f7a 100644
--- a/packages/graphql/src/queries/tx-fragments.graphql
+++ b/packages/graphql/src/queries/tx-fragments.graphql
@@ -5,17 +5,32 @@ fragment ContractItem on Contract {
fragment TransactionStatus on TransactionStatus {
__typename
+ ... on SqueezedOutStatus {
+ reason
+ }
... on SuccessStatus {
time
block {
id
header {
+ id
+ height
daHeight
+ applicationHash
+ messageReceiptRoot
+ messageReceiptCount
+ time
}
}
+ programState {
+ data
+ }
}
... on FailureStatus {
time
+ programState {
+ data
+ }
}
... on SubmittedStatus {
time
@@ -32,11 +47,11 @@ fragment TransactionInput on Input {
predicateData
txPointer
utxoId
+ witnessIndex
}
... on InputContract {
utxoId
balanceRoot
- stateRoot
txPointer
contract {
...ContractItem
@@ -62,6 +77,7 @@ fragment TransactionOutput on Output {
}
... on ContractOutput {
inputIndex
+ balanceRoot
}
... on ChangeOutput {
to
diff --git a/packages/ui/src/components/Collapsible/Collapsible.tsx b/packages/ui/src/components/Collapsible/Collapsible.tsx
index 7921f53dc..87f459326 100644
--- a/packages/ui/src/components/Collapsible/Collapsible.tsx
+++ b/packages/ui/src/components/Collapsible/Collapsible.tsx
@@ -130,7 +130,7 @@ const styles = tv({
slots: {
root: 'py-[10px]',
header: 'group grid grid-cols-[1fr_auto] grid-rows-1 gap-4 items-center',
- icon: 'transition-transform group-data-[state=closed]:hover:-rotate-180 group-data-[state=opened]:-rotate-180',
+ icon: 'transition-transform group-data-[state=opened]:-rotate-180',
content: 'mx-4 mb-2 border border-border',
body: '',
title: 'flex items-center gap-2 text-sm font-medium',
diff --git a/packages/ui/src/hooks/useVariants.tsx b/packages/ui/src/hooks/useVariants.tsx
index b7954f0c4..8122c9f71 100644
--- a/packages/ui/src/hooks/useVariants.tsx
+++ b/packages/ui/src/hooks/useVariants.tsx
@@ -2,7 +2,13 @@
import { cx } from '../utils/css';
-export type Variant = 'solid' | 'ghost' | 'outline' | 'surface' | 'link';
+export type Variant =
+ | 'solid'
+ | 'ghost'
+ | 'outline'
+ | 'surface'
+ | 'link'
+ | 'soft';
export type VariantProps = {
className?: string;
variant?: V;