Skip to content

feat(txn): add enhanced Decibel transaction details to existing transaction page#1508

Draft
gregnazario wants to merge 7 commits intomainfrom
cursor/detailed-decibel-transactions-page-ce27
Draft

feat(txn): add enhanced Decibel transaction details to existing transaction page#1508
gregnazario wants to merge 7 commits intomainfrom
cursor/detailed-decibel-transactions-page-ce27

Conversation

@gregnazario
Copy link
Contributor

@gregnazario gregnazario commented Mar 21, 2026

Description

Enhances the existing transaction detail page (/txn/:id/:tab) with richer parsing and display for transactions that involve the Decibel contract (0x50ead22afd6ffd9769e3b3d6e0e64a2a350d68e8b102c4e72e33d0b8cfdfdb06). Works on mainnet, testnet, and the decibel network — the panel appears whenever the transaction's sender, payload function, events, or state changes touch the Decibel address.

Detection logic (isDecibelTransaction):

  • Sender matches Decibel address
  • Payload function targets the Decibel contract
  • Any event originates from the Decibel contract
  • Any write-set change address matches

Changes to TransactionTitle:

  • Shows a "Decibel" badge chip next to the "Transaction" heading
  • Page metadata includes Decibel-specific title, description, and keywords

Changes to UserTransactionOverviewTab:

  • Injects a DecibelDetailsPanel above the standard overview content (collapsed by default)
  • Clicking the header expands the panel, which provides:
  1. "What Happened" Summary — Plain-English description of the transaction:

    • Function called (e.g., "Called Transfer on the coin module")
    • Transfers/deposits/withdrawals aggregated with Octa→APT conversion
    • Swaps, mints, burns described as human-readable sentences
    • Gas fees (net of storage refund) in APT
    • Number of on-chain resources modified
    • Preview of first lines shown in the collapsed accordion header
  2. Event Timeline — Events displayed as a categorized timeline with:

    • Color-coded type chips (Deposit/Withdraw/Transfer/Swap/Mint/Burn/Fee/Create/Liquidity/Stake)
    • Shortened event type names (e.g., token::MintEvent instead of full address)
    • Contract address links via HashButton
    • Inline decoded event data with amount formatting
  3. Payload Breakdown — Function call with module highlighting, type arguments, and indexed arguments

  4. State Changes Summary — Change type counts and "Modules Touched" list

New file:

  • app/pages/Transaction/Tabs/Components/DecibelDetailsPanel.tsx

Modified files:

  • app/pages/Transaction/Title.tsx — Decibel badge + metadata
  • app/pages/Transaction/Tabs/UserTransactionOverviewTab.tsx — DecibelDetailsPanel integration

Related Links

N/A

Checklist

  • TypeScript compiles (tsc --noEmit)
  • Biome lint passes
  • Biome format applied
  • All 214 tests pass (pnpm test --run)
  • Production build succeeds (pnpm build)
  • Rebased on latest main
  • No new routes added (no LLM/SEO file changes needed)
Open in Web Open in Cursor 

@netlify
Copy link

netlify bot commented Mar 21, 2026

Deploy Preview for aptos-explorer ready!

Name Link
🔨 Latest commit ecdf750
🔍 Latest deploy log https://app.netlify.com/projects/aptos-explorer/deploys/69bf43d8c8d884000900c9e4
😎 Deploy Preview https://deploy-preview-1508--aptos-explorer.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@cursor cursor bot changed the title feat(decibel): add detailed Decibel transactions page feat(txn): add enhanced Decibel transaction details to existing transaction page Mar 21, 2026
@gregnazario gregnazario reopened this Mar 22, 2026
- New route at /transactions/decibel for browsing Decibel network transactions
- DecibelNetworkStatus component showing chain ID, epoch, ledger version, block height
- DecibelTransactionCard with expandable detailed view including:
  - Function display with module highlighting
  - Events summary with type chips and full event data
  - Payload preview with type arguments and decoded arguments
  - State changes summary with change type breakdown
  - VM status display
- Auto-refresh every 10 seconds
- Pagination support
- Add /transactions/decibel to llms.txt and llms-full.txt
- Add sitemap.xml entry for Decibel transactions page
- Add drift test coverage for /transactions/decibel path
- Bump llms-full.txt revision to 10
Will instead enhance the existing transaction detail page with
Decibel-specific parsing and display.
When viewing transactions on the Decibel network (?network=decibel):

- Transaction title shows a 'Decibel Network' badge chip
- Page metadata includes Decibel-specific title, description, and keywords
- UserTransactionOverviewTab shows a DecibelDetailsPanel with:
  - Event Timeline: categorized events with color-coded type chips
    (Deposit/Withdraw/Transfer/Swap/Mint/Burn/Fee/etc), shortened
    type names, contract address links, and inline decoded data
    with amount formatting (raw + human-readable for Octa values)
  - Payload Breakdown: function displayed with module highlighting
    and linked contract address, type arguments with struct name
    emphasis, and indexed arguments with JSON viewer for complex values
  - State Changes summary: change type counts and list of touched
    modules extracted from write-set resources

The panel is visually distinct with a primary-color border and
appears above the standard overview content.
Instead of gating the enhanced details to ?network=decibel, detect
Decibel transactions by checking whether the sender, payload function
target, event sources, or change addresses match the known Decibel
contract address (0x50ead...). This makes the enhanced panel appear
on mainnet, testnet, and decibel alike.
The Decibel enhanced details section is now an Accordion that starts
collapsed. Users click the header row (Decibel chip + label + chevron)
to expand and reveal the Event Timeline, Payload Breakdown, and State
Changes sections.
The Decibel details accordion now shows a plain-English summary:
- Collapsed header: first summary line + brief preview of next lines
- Expanded: full bulleted 'What Happened' section above the technical details

The summariser parses the function call, events, and state changes into
sentences like 'Called Transfer on the coin module', 'Moved 1.5 APT out
and 1.2 APT in across 4 operations', 'Performed a token swap', etc.
Amounts are auto-converted from Octas to APT for readability.
@cursor cursor bot force-pushed the cursor/detailed-decibel-transactions-page-ce27 branch from 3354737 to ecdf750 Compare March 22, 2026 01:20
@gregnazario gregnazario requested a review from Copilot March 22, 2026 01:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the existing transaction detail page (/txn/:id/:tab) by detecting transactions that involve the Decibel contract and surfacing a collapsible “Decibel” details panel with human-readable summaries, event timeline, payload breakdown, and state-change aggregation.

Changes:

  • Add Decibel transaction detection (isDecibelTransaction) and a new DecibelDetailsPanel UI.
  • Update transaction title/metadata to show a “Decibel” badge and Decibel-specific metadata when applicable.
  • Inject the Decibel details panel into the user transaction overview tab when a transaction is detected as Decibel-related.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
app/pages/Transaction/Title.tsx Adds Decibel badge + Decibel-specific metadata based on detection logic.
app/pages/Transaction/Tabs/UserTransactionOverviewTab.tsx Conditionally renders DecibelDetailsPanel for Decibel-related transactions.
app/pages/Transaction/Tabs/Components/DecibelDetailsPanel.tsx New accordion panel that summarizes and displays Decibel-related transaction details.

Comment on lines +271 to +279
function SectionHeader({
icon,
title,
count,
}: {
icon: React.ReactNode;
title: string;
count?: number;
}) {
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SectionHeader references React.ReactNode, but this file doesn't import the React types. This will fail TypeScript compilation unless React is in scope; add import type React from "react" (or switch to ReactNode from react).

Copilot uses AI. Check for mistakes.
storageRefundOctas !== undefined
? gasFeeOctas - storageRefundOctas
: gasFeeOctas;
lines.push(`Gas fee: ${octasToApt(net.toString())} APT.`);
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

octasToApt() already appends the "APT" unit for non-zero values, but the gas fee line adds "APT" again (Gas fee: ${octasToApt(...)} APT.), which will render as "... APT APT.". Either have octasToApt return a unitless number string or remove the extra "APT" suffix in this sentence.

Suggested change
lines.push(`Gas fee: ${octasToApt(net.toString())} APT.`);
lines.push(`Gas fee: ${octasToApt(net.toString())}.`);

Copilot uses AI. Check for mistakes.
Comment on lines +824 to +833
{summary.map((line) => (
<Typography
component="li"
key={line}
variant="body2"
sx={{pl: 0.5}}
>
{line}
</Typography>
))}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The summary list uses key={line} when rendering summary.map(...). If two summary lines are identical (e.g., multiple "Minted tokens." events), React keys will collide and can cause incorrect reconciliation/warnings. Use a stable unique key (e.g., include the index) instead of the line text alone.

Copilot uses AI. Check for mistakes.
Comment on lines +254 to +261
function octasToApt(octas: string): string {
try {
const n = BigInt(octas);
const decimal = Number(n) / 1e8;
if (decimal === 0) return "0";
if (Math.abs(decimal) < 0.0001) return `${decimal.toExponential(2)} APT`;
return `${decimal.toLocaleString(undefined, {maximumFractionDigits: 8})} APT`;
} catch {
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

octasToApt converts BigInt to Number (Number(n) / 1e8), which can lose precision for values > 2^53 and can misformat large on-chain amounts. Consider formatting purely as strings/BigInt (e.g., reuse getFormattedBalanceStr / APTCurrencyValue logic) instead of converting through Number.

Suggested change
function octasToApt(octas: string): string {
try {
const n = BigInt(octas);
const decimal = Number(n) / 1e8;
if (decimal === 0) return "0";
if (Math.abs(decimal) < 0.0001) return `${decimal.toExponential(2)} APT`;
return `${decimal.toLocaleString(undefined, {maximumFractionDigits: 8})} APT`;
} catch {
/**
* Format a non-negative integer string with thousands separators.
* E.g. "1234567" -> "1,234,567".
*/
function formatIntegerWithSeparators(value: string): string {
if (value.length <= 3) return value;
const parts: string[] = [];
let i = value.length;
while (i > 3) {
parts.unshift(value.slice(i - 3, i));
i -= 3;
}
if (i > 0) {
parts.unshift(value.slice(0, i));
}
return parts.join(",");
}
function octasToApt(octas: string): string {
try {
const n = BigInt(octas);
const OCTA_PER_APT = 100000000n;
if (n === 0n) {
// Preserve existing behaviour: plain "0" without units.
return "0";
}
const negative = n < 0n;
const abs = negative ? -n : n;
// For very small amounts (< 0.0001 APT), keep exponential notation as before.
// 0.0001 APT = 10_000 octas. These values are safely representable as Number.
if (abs < 10000n) {
const decimal = Number(abs) / 1e8;
const expStr = decimal.toExponential(2);
return `${negative ? "-" : ""}${expStr} APT`;
}
const integerPart = abs / OCTA_PER_APT;
const fractionalPart = abs % OCTA_PER_APT;
let integerStr = integerPart.toString();
integerStr = formatIntegerWithSeparators(integerStr);
let fractionStr = fractionalPart.toString().padStart(8, "0");
// Trim trailing zeros to respect a maximum of 8 fraction digits.
fractionStr = fractionStr.replace(/0+$/, "");
let result = integerStr;
if (fractionStr.length > 0) {
result = `${integerStr}.${fractionStr}`;
}
if (negative) {
result = `-${result}`;
}
return `${result} APT`;
} catch {
// If parsing fails, fall back to the original string.

Copilot uses AI. Check for mistakes.
Comment on lines +476 to +488
function formatEventValue(value: string, key: string): string {
const lowerKey = key.toLowerCase();
if (
(lowerKey.includes("amount") || lowerKey === "apt" || lowerKey === "fee") &&
/^\d+$/.test(value) &&
value.length > 4
) {
const num = BigInt(value);
const decimal = Number(num) / 1e8;
if (decimal >= 0.0001 && decimal < 1e15) {
return `${decimal.toLocaleString(undefined, {maximumFractionDigits: 8})} (${value})`;
}
}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatEventValue also converts BigInt to Number for display, which will lose precision for large amounts and may show an incorrect decimal representation. Prefer BigInt/string-based formatting (consistent with CurrencyValue.getFormattedBalanceStr) and/or only apply decimal conversion when you know the asset decimals.

Copilot uses AI. Check for mistakes.
import TitleHashButton, {HashType} from "../../components/TitleHashButton";
import {TransactionType} from "../../components/TransactionType";
import {truncateAddress} from "../../utils";
import {isDecibelTransaction} from "./Tabs/Components/DecibelDetailsPanel";
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Title.tsx imports isDecibelTransaction from DecibelDetailsPanel.tsx, which also imports MUI Accordion + several icon components. This risks pulling the whole panel (and icons) into the title bundle even when only the detection helper is needed. Consider moving DECIBEL_ADDRESS + isDecibelTransaction into a lightweight decibelTransaction.ts utility module (no UI imports) that both Title and DecibelDetailsPanel can import.

Suggested change
import {isDecibelTransaction} from "./Tabs/Components/DecibelDetailsPanel";
import {isDecibelTransaction} from "./Tabs/Components/decibelTransaction";

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +30

/**
* Detect whether a transaction involves the Decibel contract.
* Checks the sender, the payload function target, and event sources.
*/
export function isDecibelTransaction(transaction: Types.Transaction): boolean {
const normalizedDecibel = tryStandardizeAddress(DECIBEL_ADDRESS);
if (!normalizedDecibel) return false;
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isDecibelTransaction recomputes tryStandardizeAddress(DECIBEL_ADDRESS) on every call. Since DECIBEL_ADDRESS is constant, consider hoisting the standardized value to module scope (and handling the undefined case once) to avoid repeated parsing work on each render.

Suggested change
/**
* Detect whether a transaction involves the Decibel contract.
* Checks the sender, the payload function target, and event sources.
*/
export function isDecibelTransaction(transaction: Types.Transaction): boolean {
const normalizedDecibel = tryStandardizeAddress(DECIBEL_ADDRESS);
if (!normalizedDecibel) return false;
const NORMALIZED_DECIBEL_ADDRESS = tryStandardizeAddress(DECIBEL_ADDRESS);
/**
* Detect whether a transaction involves the Decibel contract.
* Checks the sender, the payload function target, and event sources.
*/
export function isDecibelTransaction(transaction: Types.Transaction): boolean {
if (!NORMALIZED_DECIBEL_ADDRESS) return false;
const normalizedDecibel = NORMALIZED_DECIBEL_ADDRESS;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants